/*==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 "plResponderGetComp.h"

#include <algorithm>

#include "MaxMain/plMaxNodeBase.h"
#include "resource.h"
#include "plComponentBase.h"
#include "MaxMain/plMaxAccelerators.h"

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

bool plResponderGetComp::GetComp(IParamBlock2 *pb, int nodeID, int compID, ClassIDs *classIDs)
{
    fPB = pb;
    fNodeID = nodeID;
    fCompID = compID;
    fClassIDs = classIDs;

    plMaxAccelerators::Disable();

    int ret = DialogBox(hInstance,
                        MAKEINTRESOURCE(IDD_COMP_RESPOND_ANIMPICK),
                        GetCOREInterface()->GetMAXHWnd(),
                        ForwardDlgProc);

    plMaxAccelerators::Enable();

    return (ret != 0);
}

plComponentBase *plResponderGetComp::GetSavedComp(IParamBlock2 *pb, int nodeID, int compID, bool convertTime)
{
    plMaxNodeBase *node = (plMaxNodeBase*)pb->GetReferenceTarget(nodeID);
    // This value could be whack, only use if node is valid
    plMaxNodeBase *comp = (plMaxNodeBase*)pb->GetReferenceTarget(compID);

    if (!node || (convertTime && !node->CanConvert()) || !comp)
        return nil;

    int numComps = node->NumAttachedComponents();
    for (int i = 0; i < numComps; i++)
    {
        plComponentBase *thisComp = node->GetAttachedComponent(i);
        if (thisComp->GetINode() == comp)
            return thisComp;
    }

    return nil;
}
void plResponderGetComp::IFindCompsRecur(plMaxNodeBase *node, NodeSet& nodes)
{
    plComponentBase *comp = node->ConvertToComponent();
    if (comp)
    {
        // If we're not filtering, or we are and this component is in our accepted list, add it
        if (!fClassIDs ||
            std::find(fClassIDs->begin(), fClassIDs->end(), comp->ClassID()) != fClassIDs->end())
        {
            nodes.insert(node);
        }
    }

    for (int i = 0; i < node->NumberOfChildren(); i++)
        IFindCompsRecur((plMaxNodeBase*)node->GetChildNode(i), nodes);
}

void plResponderGetComp::ILoadNodes(plMaxNodeBase *compNode, HWND hDlg)
{
    HWND hNodes = GetDlgItem(hDlg, IDC_OBJ_LIST);
    ListBox_ResetContent(hNodes);

    plComponentBase *comp = compNode ? compNode->ConvertToComponent() : nil;
    if (!comp)
        return;

    plMaxNodeBase *savedNode = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID);

    for (int i = 0; i < comp->NumTargets(); i++)
    {
        plMaxNodeBase *node = comp->GetTarget(i);
        if (node)
        {
            int idx = ListBox_AddString(hNodes, node->GetName());
            ListBox_SetItemData(hNodes, idx, node);

            if (savedNode == node)
                ListBox_SetCurSel(hNodes, idx);
        }
    }
}

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

BOOL plResponderGetComp::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        {
            NodeSet nodes;
            IFindCompsRecur((plMaxNodeBase*)GetCOREInterface()->GetRootNode(), nodes);

            HWND hComps = GetDlgItem(hDlg, IDC_COMP_LIST);

            plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);

            for (NodeSet::iterator it = nodes.begin(); it != nodes.end(); it++)
            {
                int idx = ListBox_AddString(hComps, (*it)->GetName());
                ListBox_SetItemData(hComps, idx, *it);

                if (*it == node)
                    ListBox_SetCurSel(hComps, idx);
            }

            ILoadNodes(node, hDlg);
        }
        return TRUE;

    case WM_COMMAND:
        if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, 0);
            return TRUE;
        }
        else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
        {
            // Get the selected node
            HWND hNode = GetDlgItem(hDlg, IDC_OBJ_LIST);
            int idx = ListBox_GetCurSel(hNode);
            plMaxNodeBase *node = nil;
            if (idx != LB_ERR)
                node = (plMaxNodeBase*)ListBox_GetItemData(hNode, idx);

            // Get the selected component
            HWND hComp = GetDlgItem(hDlg, IDC_COMP_LIST);
            idx = ListBox_GetCurSel(hComp);
            plMaxNodeBase *comp = nil;
            if (idx != LB_ERR)
                comp = (plMaxNodeBase*)ListBox_GetItemData(hComp, idx);

            // If both were selected, save them in the PB and as the last setting
            if (node && comp)
            {
#if 0
                if (fType == kFindAnim)
                {
                    fLastAnimObj = obj->GetHandle();
                    fLastAnimComp = comp->GetHandle();
                }
                else if (fType == kFindSound)
                {
                    fLastSoundObj = obj->GetHandle();
                    fLastSoundComp = comp->GetHandle();
                }
#endif
                fPB->SetValue(fNodeID, 0, (ReferenceTarget*)node);
                fPB->SetValue(fCompID, 0, (ReferenceTarget*)comp);
                
                EndDialog(hDlg, 1);
            }
            else
                EndDialog(hDlg, 0);

            return TRUE;
        }
        else if (HIWORD(wParam) == LBN_SELCHANGE)
        {
            if (LOWORD(wParam) == IDC_COMP_LIST)
            {
                int idx = ListBox_GetCurSel((HWND)lParam);
                plMaxNodeBase *node = (plMaxNodeBase*)ListBox_GetItemData((HWND)lParam, idx);
                ILoadNodes(node, hDlg);
            }
        }
        break;
    }

    return FALSE;
}

///////////////////////////////////////////////////////////////////////////

#include "plPickNode.h"

void plResponderCompNode::Init(IParamBlock2 *pb, int compID, int nodeID, int compResID, int nodeResID, ClassIDs *compCIDs)
{
    fPB = pb;
    fCompID = compID;
    fNodeID = nodeID;
    fCompResID = compResID;
    fNodeResID = nodeResID;

    if (compCIDs)
        fCompCIDs = *compCIDs;
}

void plResponderCompNode::InitDlg(HWND hWnd)
{
    IValidate();

    IUpdateNodeButton(hWnd);
    IUpdateCompButton(hWnd);
}

bool plResponderCompNode::IValidate()
{
    plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);
    plComponentBase *comp = savedComp ? savedComp->ConvertToComponent() : nil;
    plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID);
    if (comp && node)
    {
        // Make sure the selected comp has the correct CID
        if (fCompCIDs.size() > 0)
        {
            Class_ID compCID = comp->ClassID();
            bool foundCID = false;
            for (int i = 0; i < fCompCIDs.size(); i++)
            {
                if (fCompCIDs[i] == compCID)
                    foundCID = true;
            }

            if (!foundCID)
            {
                fPB->SetValue(fCompID, 0, (INode*)nil);
                fPB->SetValue(fNodeID, 0, (INode*)nil);
                return false;
            }
        }

        // Make sure the comp is really attached to the node
        if (comp->IsTarget(node))
            return true;
        else
            fPB->SetValue(fNodeID, 0, (INode*)nil);
    }

    return false;
}

void plResponderCompNode::CompButtonPress(HWND hWnd)
{
    plPick::Node(fPB, fCompID, &fCompCIDs, true, false);

    IUpdateCompButton(hWnd);
    IUpdateNodeButton(hWnd);
}

void plResponderCompNode::NodeButtonPress(HWND hWnd)
{
    plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);
    plComponentBase *comp = node ? node->ConvertToComponent() : nil;
    if (comp)
        plPick::CompTargets(fPB, fNodeID, comp);

    IUpdateNodeButton(hWnd);
}

void plResponderCompNode::IUpdateCompButton(HWND hWnd)
{
    HWND hComp = GetDlgItem(hWnd, fCompResID);

    plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);

    if (savedComp)
        SetWindowText(hComp, savedComp->GetName());
    else
        SetWindowText(hComp, "(none)");
}

void plResponderCompNode::IUpdateNodeButton(HWND hWnd)
{
    HWND hNode = GetDlgItem(hWnd, fNodeResID);

    // If there is no component, disable the node button
    plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);
    plComponentBase *comp = node ? node->ConvertToComponent() : nil;
    if (!comp)
    {
        EnableWindow(hNode, FALSE);
        SetWindowText(hNode, "(none)");
        return;
    }
    EnableWindow(hNode, TRUE);

    plMaxNodeBase *objNode = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID);
    if (objNode && comp->IsTarget(objNode))
        SetWindowText(hNode, objNode->GetName());
    else
        SetWindowText(hNode, "(none)");
}

bool plResponderCompNode::GetCompAndNode(plComponentBase*& comp, plMaxNodeBase*& node)
{
    if (!IValidate())
    {
        comp = nil;
        node = nil;
        return false;
    }

    plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID);
    comp = savedComp ? savedComp->ConvertToComponent() : nil;
    node = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID);

    return true;
}