#include "stdafx.h"
#include "MAC.h"
#include "FormatArray.h"
#include "FormatPlugin.h"
#include "FormatAPE.h"
#include "MACFile.h"
#include "MACDlg.h"

using namespace APE;

CFormatArray::CFormatArray(CMACDlg * pMACDlg)
{
    m_pMACDlg = pMACDlg;
}

CFormatArray::~CFormatArray()
{
    Unload();
}

bool CFormatArray::Load()
{
    // remove all
    Unload();

    // APE
    m_aryFormats.Add(new CFormatAPE(0));

    // plugins
    CStringArray aryFiles;
    ListFiles(&aryFiles, GetProgramPath() + _T("External\\"));
    for (int z = 0; z < aryFiles.GetSize(); z++)
    {
        if (CFilename(aryFiles[z]).GetExtension() == _T(".apx"))
        {
            CFormatPlugin * pPlugin = new CFormatPlugin(m_pMACDlg, static_cast<int>(m_aryFormats.GetSize()));
            pPlugin->Load(aryFiles[z]);
            m_aryFormats.Add(pPlugin);
        }
    }

    return true;
}

bool CFormatArray::Unload()
{
    for (int z = 0; z < m_aryFormats.GetSize(); z++)
    {
        APE_SAFE_DELETE(m_aryFormats[z])
    }
    m_aryFormats.RemoveAll();

    ClearMenuCache();

    return true;
}

void CFormatArray::ClearMenuCache()
{
    while (m_aryMenus.GetSize() > 0)
    {
        m_aryMenus[m_aryMenus.GetUpperBound()]->DestroyMenu();
        APE_SAFE_DELETE(m_aryMenus[m_aryMenus.GetUpperBound()])
        m_aryMenus.RemoveAt(m_aryMenus.GetUpperBound());
    }
}

bool CFormatArray::FillCompressionMenu(CMenu * pMenu)
{
    // this should only be called if we loaded and have formats
    if (m_aryFormats.IsEmpty())
        return false;

    // clear the menu cache
    ClearMenuCache();

    // primary (not nested)
    m_aryFormats[0]->BuildMenu(pMenu, ID_SET_COMPRESSION_FIRST + (100 * (0 + 1)));

    // non-primary
    pMenu->AppendMenu(MF_SEPARATOR);

    CMenu * pExternalMenu = new CMenu; pExternalMenu->CreatePopupMenu(); m_aryMenus.Add(pExternalMenu);
    pMenu->AppendMenu(MF_POPUP, reinterpret_cast<UINT_PTR>(pExternalMenu->GetSafeHmenu()), _T("E&xternal"));

    if (m_aryFormats.GetSize() <= 1)
    {
        pExternalMenu->AppendMenu(MF_STRING | MF_GRAYED, 0, _T("n/a"));
    }
    else
    {
        for (int z = 1; z < m_aryFormats.GetSize(); z++)
        {
            // create
            CMenu * pNewMenu = new CMenu;
            pNewMenu->CreatePopupMenu();

            // put in the array (even if build doesn't work so it gets deleted later)
            // we originally put the delete here but that made Clang lose it during analysis
            m_aryMenus.Add(pNewMenu);

            // build
            if (m_aryFormats[z]->BuildMenu(pNewMenu, ID_SET_COMPRESSION_FIRST + (100 * (z + 1))))
            {
                // append the menu
                pExternalMenu->AppendMenu(MF_POPUP, reinterpret_cast<UINT_PTR>(pNewMenu->GetSafeHmenu()), m_aryFormats[z]->GetName());
            }
        }
    }

    return true;
}

bool CFormatArray::ProcessCompressionMenu(int nID)
{
    nID -= ID_SET_COMPRESSION_FIRST;

    int nIndex = (nID / 100);
    int nCommand = nID - (nIndex * 100);
    nIndex -= 1;

    if (nIndex == -1)
    {
        // local commands (i.e. "Refresh Plugins")
    }
    else if ((nIndex >= 0) && (nIndex < m_aryFormats.GetSize()))
    {
        // plugin commands
        m_aryFormats[nIndex]->ProcessMenuCommand(nCommand);
    }

    return true;
}

int CFormatArray::Process(MAC_FILE * pInfo)
{
    int nRetVal = ERROR_INVALID_INPUT_FILE;

    // use the format
    IFormat * pFormat = pInfo->m_pFormat;

    // if we found a format that can do the job, use it to do the work
    if (pFormat != APE_NULL)
    {
        nRetVal = pFormat->Process(pInfo);
    }

    return nRetVal;
}

IFormat * CFormatArray::GetFormat(const CString & strName)
{
    for (int z = 0; z < m_aryFormats.GetSize(); z++)
    {
        if (m_aryFormats[z]->GetName() == strName)
            return m_aryFormats[z];
    }

    return APE_NULL;
}

CString CFormatArray::GetOutputExtension(APE_MODES Mode, const CString & strInputFilename, int nLevel, IFormat * pFormat)
{
    CString strExtension;

    if (Mode == MODE_MAKE_APL)
    {
        strExtension = _T(".apl");
    }
    else if (pFormat != APE_NULL)
    {
        strExtension = pFormat->GetOutputExtension(Mode, strInputFilename, nLevel);
    }

    return strExtension;
}

bool CFormatArray::GetInputExtensions(CStringArrayEx & aryExtensions)
{
    // clear the list
    aryExtensions.RemoveAll();

    // build a map of extensions (so we remove duplicates)
    CMap<CString, LPCTSTR, bool, bool> mapExtensions;
    for (int z = 0; z < m_aryFormats.GetSize(); z++)
    {
        CString strExtensions = m_aryFormats[z]->GetInputExtensions(theApp.GetSettings()->GetMode());
        CStringArrayEx aryFormatExtensions; aryFormatExtensions.InitFromList(strExtensions, _T(";"));
        for (int nFormatExtension = 0; nFormatExtension < aryFormatExtensions.GetSize(); nFormatExtension++)
        {
            // "clean" the extension
            CString strExtension = aryFormatExtensions[nFormatExtension];
            if (strExtension.Left(1) != _T(".")) strExtension = _T(".") + strExtension;
            strExtension.MakeLower();

            // add it to the map
            mapExtensions.SetAt(strExtension, true);
        }
    }

    // add all the extensions to the array
    POSITION Pos = mapExtensions.GetStartPosition();
    while (Pos)
    {
        CString strExtension; bool bTemp = false;
        mapExtensions.GetNextAssoc(Pos, strExtension, bTemp);
        aryExtensions.Add(strExtension);
    }

    // sort
    aryExtensions.SortAscending();

    return true;
}

CString CFormatArray::GetOpenFilesFilter(bool bAddAllFiles)
{
    // filter
    CString strFilter;

    // get the input extensions
    CStringArrayEx aryExtensions;
    GetInputExtensions(aryExtensions);

    // build the supported extensions expression
    CString strSupportedExtensions;
    for (int z = 0; z < aryExtensions.GetSize(); z++)
        strSupportedExtensions += _T("*") + aryExtensions[z] + _T(";");
    strSupportedExtensions.TrimRight(_T(";"));
    strFilter.Format(_T("All Supported Files (%s)|%s|"), strSupportedExtensions.GetString(), strSupportedExtensions.GetString());

    // build the list on a per-format basis
    for (int z = 0; z < m_aryFormats.GetSize(); z++)
    {
        // skip invalid formats
        if (m_aryFormats[z]->GetValid() == false)
            continue;

        // get the format's extensions
        aryExtensions.InitFromList(m_aryFormats[z]->GetInputExtensions(theApp.GetSettings()->GetMode()), _T(";"));

        if (aryExtensions.GetSize() > 0)
        {
            // build the extension list
            CString strFormatExtensions;
            for (int nExtension = 0; nExtension < aryExtensions.GetSize(); nExtension++)
                strFormatExtensions += _T("*") + aryExtensions[nExtension] + _T(";");
            strFormatExtensions.TrimRight(_T(";"));

            // build the filter string
            CString strFormatFilter;
            strFormatFilter.Format(_T("%s Files (%s)|%s|"), m_aryFormats[z]->GetName().GetString(), strFormatExtensions.GetString(), strFormatExtensions.GetString());
            strFilter += strFormatFilter;
        }
    }

    // all files
    if (bAddAllFiles)
        strFilter += _T("All Files (*.*)|*.*|");

    // terminate
    strFilter.TrimRight(_T("|"));
    strFilter += _T("||");

    // return
    return strFilter;
}


IFormat * CFormatArray::GetFormat(MAC_FILE * pInfo)
{
    IFormat * pFormat = APE_NULL;

    if ((pInfo->m_Mode == MODE_DECOMPRESS) || (pInfo->m_Mode == MODE_VERIFY))
    {
        // find the first plugin that supports this file type
        CString strExtension = CFilename(pInfo->m_strInputFilename).GetExtension();
        for (int z = 0; z < m_aryFormats.GetSize(); z++)
        {
            CStringArrayEx aryExtensions;
            aryExtensions.InitFromList(m_aryFormats[z]->GetInputExtensions(pInfo->m_Mode), _T(";"));
            if (aryExtensions.Find(strExtension) != -1)
            {
                pFormat = m_aryFormats[z];
                break;
            }
        }
    }
    else
    {
        pFormat = GetFormat(theApp.GetSettings()->GetFormat());
    }

    return pFormat;
}

IFormat * CFormatArray::GetFormatFromInputType(const CString & strInputExtension)
{
    for (int z = 0; z < m_aryFormats.GetSize(); z++)
    {
        CStringArrayEx aryExtensions;
        aryExtensions.InitFromList(m_aryFormats[z]->GetInputExtensions(MODE_DECOMPRESS), _T(";"));
        if (aryExtensions.Find(strInputExtension) != -1)
        {
            return m_aryFormats[z];
        }
    }

    return APE_NULL;
}
