/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "HeadSpin.h"
#include "plTextureSearch.h"
#include "resource.h"
#include "hsUtils.h"

#define PB2Export __declspec( dllexport )   // Because I don't feel like including all the paramblock crap
#include "pbbitmap.h"
#include "bmmlib.h"
#include "IMtlEdit.h"

#include "plMtlCollector.h"
#include "plMaxAccelerators.h"
#include "MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h"
#ifdef MAXASS_AVAILABLE
#include "../../AssetMan/PublicInterface/MaxAssInterface.h"
#endif

// Not a class member so we don't have to make everyone who uses this know about AssetMan
#ifdef MAXASS_AVAILABLE
static jvUniqueId gAssetID;
#endif

plTextureSearch::plTextureSearch() : fDlg(NULL)
{
#ifdef MAXASS_AVAILABLE
    gAssetID.SetEmpty();
#endif
    memset(fFileName, 0, sizeof(fFileName));
}

plTextureSearch& plTextureSearch::Instance()
{
    static plTextureSearch theInstance;
    return theInstance;
}

void plTextureSearch::Toggle()
{
    if (!fDlg)
    {
        fDlg = CreateDialog(hInstance,
                            MAKEINTRESOURCE(IDD_FIND_TEXTURE),
                            GetCOREInterface()->GetMAXHWnd(),
                            ForwardDlgProc);

        HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST);
        LVCOLUMN lvc;
        lvc.mask = LVCF_TEXT;
        lvc.pszText = "Material";
        ListView_InsertColumn(hList, 0, &lvc);

        lvc.pszText = "Layer";
        ListView_InsertColumn(hList, 1, &lvc);

        lvc.pszText = "Texture";
        ListView_InsertColumn(hList, 2, &lvc);

        IUpdateTextures(kUpdateLoadList);

        GetCOREInterface()->RegisterDlgWnd(fDlg);
        ShowWindow(fDlg, SW_SHOW);
    }
    else
    {
        DestroyWindow(fDlg);
        fDlg = NULL;
    }
}


BOOL plTextureSearch::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    return Instance().DlgProc(hDlg, msg, wParam, lParam);
}

BOOL plTextureSearch::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_ACTIVATE:
        if (LOWORD(wParam) == WA_INACTIVE)
            plMaxAccelerators::Enable();
        else
            plMaxAccelerators::Disable();
        return TRUE;

    case WM_COMMAND:
        if (HIWORD(wParam) == BN_CLICKED)
        {
            int id = LOWORD(wParam);
            if (id == IDCANCEL)
            {
                Toggle();
                return TRUE;
            }
            else if (id == IDC_UPDATE_BUTTON)
            {
                IUpdateTextures(kUpdateLoadList);
                return TRUE;
            }
            else if (id == IDC_REPLACE_ALL_BUTTON)
            {
                if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes)
                {
                    IUpdateTextures(kUpdateReplace);
                }
                return TRUE;
            }
            else if (id == IDC_SET_ALL_BUTTON)
            {
                if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes)
                {
                    IUpdateTextures(kUpdateSetSize);
                }
                return TRUE;
            }
            else if (id == IDC_REPLACE_BUTTON)
            {
                IPickReplaceTexture();
                return TRUE;
            }
        }
        else if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_FIND_EDIT)
        {
            bool findText = (SendDlgItemMessage(hDlg, IDC_FIND_EDIT, EM_LINELENGTH, 0, 0) > 0);
            bool replace = (fFileName[0] != '\0');

            EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_BUTTON), findText);
            EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_ALL_BUTTON), findText && replace);

            return TRUE;
        }
        break;

    case WM_NOTIFY:
        {
            NMHDR* nmhdr = (NMHDR*)lParam;
            // User double-clicked a material in the texture list
            if (nmhdr->idFrom == IDC_TEXTURE_LIST && nmhdr->code == NM_DBLCLK)
            {
                NMITEMACTIVATE* itema = (NMITEMACTIVATE*)lParam;
                if (itema->iItem != -1)
                {
                    // Get the material the user clicked on
                    HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST);
                    LV_ITEM item;
                    item.iItem = itema->iItem;
                    item.mask = LVIF_PARAM;
                    ListView_GetItem(hList, &item);

                    Mtl* mtl = (Mtl*)item.lParam;

                    // Make sure the material is still in the scene (paranoid check)
                    MtlSet mtls;
                    plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl);
                    if (mtls.find(mtl) != mtls.end())
                    {
                        // Put the material in the current slot of the material editor
                        IMtlEditInterface* mtlInterface = GetMtlEditInterface();
                        int slot = mtlInterface->GetActiveMtlSlot();
                        mtlInterface->PutMtlToMtlEditor(mtl, slot);
                    }
                }

                return TRUE;
            }
        }
        break;
    }

    return FALSE;
}

static int FloorPow2(int value)
{
    int v;
    for (v = 1; v <= value; v <<= 1);
    return v >> 1;
}

void plTextureSearch::IUpdateTextures(plTextureSearch::Update update)
{
    MtlSet mtls;
    plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl);

    char searchStr[256];
    GetDlgItemText(fDlg, IDC_FIND_EDIT, searchStr, sizeof(searchStr));
    strlwr(searchStr);

    HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST);
    ListView_DeleteAllItems(hList);

    int sizeX = -1, sizeY = -1;

    HWND hCombo = GetDlgItem(fDlg, IDC_SIZE_COMBO);

    // If we're updating the size, get whatever the user selected
    if (update == kUpdateSetSize)
    {
        int sel = ComboBox_GetCurSel(hCombo);
        UInt32 data = ComboBox_GetItemData(hCombo, sel);
        sizeX = LOWORD(data);
        sizeY = HIWORD(data);
    }

    MtlSet::iterator it = mtls.begin();
    for (; it != mtls.end(); it++)
    {
        Mtl *mtl = (*it);

        LayerSet layers;
        plMtlCollector::GetMtlLayers(mtl, layers);

        LayerSet::iterator layerIt = layers.begin();
        for (; layerIt != layers.end(); layerIt++)
        {
            plPlasmaMAXLayer *layer = (*layerIt);

            int numBitmaps = layer->GetNumBitmaps();

            for (int i = 0; i < numBitmaps; i++)
            {
                PBBitmap *pbbm = layer->GetPBBitmap(i);
                if (pbbm)
                {
                    const char *name = pbbm->bi.Filename();
                    if (name && *name != '\0')
                    {
                        char buf[256];
                        strncpy(buf, name, sizeof(buf));
                        strlwr(buf);

                        // If we don't have a search string, or we do and it was
                        // found in the texture name, add the texture to the list.
                        if (searchStr[0] == '\0' || strstr(buf, searchStr))
                        {
                            if (update == kUpdateLoadList)
                            {
                                LVITEM item = {0};
                                item.mask = LVIF_TEXT | LVIF_PARAM;
                                item.pszText = mtl->GetName();
                                item.lParam = (LPARAM)mtl;  // A little dangerous, since the user could delete this
                                int idx = ListView_InsertItem(hList, &item);

                                ListView_SetItemText(hList, idx, 1, layer->GetName());
                                ListView_SetItemText(hList, idx, 2, (char*)name);

                                // If size is uninitialized or the same as the last, keep size
                                if ((sizeX == -1 && sizeY == -1) || (sizeX == pbbm->bi.Width() && sizeY == pbbm->bi.Height()))
                                {
                                    sizeX = pbbm->bi.Width();
                                    sizeY = pbbm->bi.Height();
                                }
                                // Otherwise clear it
                                else
                                {
                                    sizeX = sizeY = 0;
                                }
                            }
                            else if (update == kUpdateReplace)
                            {
#ifdef MAXASS_AVAILABLE
                                layer->SetBitmapAssetId(gAssetID, i);
#endif

                                BitmapInfo info;
                                info.SetName(fFileName);
                                layer->SetBitmap(&info, i);
                            }
                            else if (update == kUpdateSetSize)
                            {
                                layer->SetExportSize(sizeX, sizeY);
                            }
                        }
                    }
                }
            }
        }
    }

    if (update == kUpdateLoadList)
    {
        HWND hButton = GetDlgItem(fDlg, IDC_SET_ALL_BUTTON);
        ComboBox_ResetContent(hCombo);

        // If all bitmaps are the same size, enable resizing
        if (sizeX != -1 && sizeX != 0)
        {
            sizeX = FloorPow2(sizeX);
            sizeY = FloorPow2(sizeY);

            char buf[256];

            while (sizeX >= 4 && sizeY >= 4)
            {
                sprintf(buf, "%d x %d", sizeX, sizeY);
                int idx = ComboBox_AddString(hCombo, buf);
                ComboBox_SetItemData(hCombo, idx, MAKELPARAM(sizeX, sizeY));

                sizeX >>= 1;
                sizeY >>= 1;
            }

            ComboBox_SetCurSel(hCombo, 0);

            EnableWindow(hCombo, TRUE);
            EnableWindow(hButton, TRUE);
        }
        else
        {
            EnableWindow(hCombo, FALSE);
            EnableWindow(hButton, FALSE);
        }
    }

    int autoSizeType = LVSCW_AUTOSIZE;
    if (ListView_GetItemCount(hList) == 0)
        autoSizeType = LVSCW_AUTOSIZE_USEHEADER;

    ListView_SetColumnWidth(hList, 0, autoSizeType);
    ListView_SetColumnWidth(hList, 1, autoSizeType);
    ListView_SetColumnWidth(hList, 2, autoSizeType);
}

void plTextureSearch::IPickReplaceTexture()
{
    fFileName[0] = '\0';

    // if we have the assetman plug-in, then try to use it, unless shift is held down
#ifdef MAXASS_AVAILABLE
    MaxAssInterface* maxAssInterface = GetMaxAssInterface();
    if (maxAssInterface && !(GetKeyState(VK_SHIFT) & 0x8000))
    {
        maxAssInterface->OpenBitmapDlg(gAssetID, fFileName, sizeof(fFileName));
    }
    else
    {
        gAssetID.SetEmpty();
#endif
        BitmapInfo bi;
        TheManager->SelectFileInput(&bi, GetCOREInterface()->GetMAXHWnd(), _T("Select Bitmap Image File"));
        strcpy(fFileName, bi.Filename());
#ifdef MAXASS_AVAILABLE
    }
#endif

    if (fFileName[0] == '\0')
    {
        SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, "(none)");
        EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), FALSE);
    }
    else
    {
        char fname[_MAX_FNAME+_MAX_EXT], ext[_MAX_EXT];
        _splitpath(fFileName, NULL, NULL, fname, ext);
        strcat(fname, ext);

        SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, fname);
        EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), TRUE);
    }
}