/*
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant 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 2 of the License, or
(at your option) any later version.

GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

// BrushScript stuff
//

/*!
\todo is this still used / in working state?
should we cleanup and remove it for good
*/

#include "stdafx.h"
#include "gtkmisc.h"

//
struct SVariableDef
{
  CString m_strName;
  CString m_strInput;
  float m_fValue;
};

struct SVecVariableDef
{
  CString m_strName;
  CString m_strInput;
  vec3_t m_vValue;
};



const int MAX_VARIABLES = 64;

brush_t* g_pHold1 = NULL;
brush_t* g_pHold2 = NULL;
brush_t* g_pHold3 = NULL;
bool g_bRotateAroundSelection;
int g_nVariableCount;
int g_nVecVariableCount;
int g_nLoopCounter;
float g_fDefault = 9999.9f;
vec3_t g_vDefault;
bool g_bStartLoop;
char* g_pLooper;
bool g_bKeepGoing;

SVariableDef g_Variables[MAX_VARIABLES];
SVecVariableDef g_VecVariables[MAX_VARIABLES];

void InitForScriptRun()
{
  g_pHold1 = NULL;
  g_pHold2 = NULL;
  g_pHold3 = NULL;
  g_bRotateAroundSelection = true;
  g_nVariableCount = 0;
  g_nVecVariableCount = 0;
  g_nLoopCounter = 0;
  g_bStartLoop = false;
  g_pLooper = NULL;
  g_bKeepGoing = true;
}

void AddVariable(const char* pName, float fValue, const char* pInput = NULL)
{
  if (g_nVariableCount < MAX_VARIABLES)
  {
    g_Variables[g_nVariableCount].m_strName = pName;
    g_Variables[g_nVariableCount].m_strName.MakeLower();
    g_Variables[g_nVariableCount].m_fValue = fValue;
    if (pInput)
      g_Variables[g_nVariableCount].m_strInput = pInput;
    g_nVariableCount++;
  }
  else
    gtk_MessageBox(g_pParentWnd->m_pWidget, "Maximum script variable limit reached!");
}

float VariableValue(const char* pName)
{
  CString strName = pName;
  strName.MakeLower();
  for (int n = 0; n < g_nVariableCount; n++)
  {
    if (strName == g_Variables[n].m_strName)
      return g_Variables[n].m_fValue;
  }
  //strName.Format("Reference to non-existant varirable %s", pName);
  //g_pParentWnd->MessageBox(strName);
  return g_fDefault;
}

void SetVariableValue(const char* pName, float fValue)
{
  CString strName = pName;
  strName.MakeLower();
  for (int n = 0; n < g_nVariableCount; n++)
  {
    if (strName == g_Variables[n].m_strName)
      g_Variables[n].m_fValue = fValue;
  }
}



void AddVectorVariable(const char* pName, const char* pInput = NULL)
{
  if (g_nVecVariableCount < MAX_VARIABLES)
  {
    g_VecVariables[g_nVecVariableCount].m_strName = pName;
    g_VecVariables[g_nVecVariableCount].m_strName.MakeLower();
    if (pInput)
      g_VecVariables[g_nVariableCount].m_strInput = pInput;
    g_nVecVariableCount++;
  }
  else
    gtk_MessageBox(g_pParentWnd->m_pWidget, "Maximum script variable limit reached!");
}

void VectorVariableValue(const char* pName, vec3_t& v)
{
  CString strName = pName;
  strName.MakeLower();
  for (int n = 0; n < g_nVecVariableCount; n++)
  {
    if (strName == g_VecVariables[n].m_strName)
    {
      VectorCopy(g_VecVariables[n].m_vValue, v);
      return;
    }
  }
  strName.Format("Reference to non-existant variable %s", pName);
  gtk_MessageBox(g_pParentWnd->m_pWidget, strName);
}

void SetVectorVariableValue(const char* pName, vec3_t v)
{
  CString strName = pName;
  strName.MakeLower();
  for (int n = 0; n < g_nVecVariableCount; n++)
  {
    if (strName == g_VecVariables[n].m_strName)
      VectorCopy(v, g_VecVariables[n].m_vValue);
  }
}





// commands
//
// _CopySelected(nHoldPos)  
// copies selected brush to hold spot 1, 2 or 3
//
// _MoveSelected(x, y, z)
// moves selected brush by coords provided
//
// _RotateSelected(x, y, z)
// rotates selected brush by coords provided
//
// _MoveHold(nHoldPos, x, y, z)
// moves brush in hold pos by coords provided
//
// _RotateHold(nHoldPos, x, y, z)
// rotates brush in hold pos by coords provided
//
// _CopyToMap(nHoldPos)
// copies hold brush to map
//
// _CopyAndSelect(nHoldPos)
// copies hold brush to map and selects it
//
// _Input(VarName1, ... VarNamennn)
// inputs a list of values from the user
//

typedef void (PFNScript)(char*&);


struct SBrushScript
{
  const char* m_pName;
  PFNScript* m_pProc;
};


const char* GetParam(char*& pBuffer)
{
  static CString strParam;
  bool bStringMode = false;

  while (*pBuffer != (char)NULL && isspace(*pBuffer))   // skip and whitespace
    pBuffer++;

  if (*pBuffer == '(') // if it's an opening paren, skip it
    pBuffer++;

  if (*pBuffer == '\"') // string ?
  {
    pBuffer++;
    bStringMode = true;
  }

  strParam = "";

  if (bStringMode)
  {
    while (*pBuffer != (char)NULL && *pBuffer != '\"')
      strParam += *pBuffer++;
  }
  else
  {
    while (*pBuffer != (char)NULL && *pBuffer != ' ' && *pBuffer != ')' && *pBuffer != ',')
      strParam += *pBuffer++;
  }

  if (*pBuffer != (char)NULL)   // skip last char
    pBuffer++;

  if (strParam.GetLength() > 0)
  {
    if (strParam.GetAt(0) == '$') // ? variable name
    {
      float f = VariableValue(strParam);
      if (f != g_fDefault)
        strParam.Format("%f", f);
    }
  }

  return strParam;
}

brush_t* CopyBrush(brush_t* p)
{                            
  brush_t* pCopy = Brush_Clone(p);
	//Brush_AddToList (pCopy, &active_brushes);
	//Entity_LinkBrush (world_entity, pCopy);
	Brush_Build(pCopy, false);

  return pCopy;
}


void CopySelected(char*& pBuffer)
{
  // expects one param
  CString strParam = GetParam(pBuffer);
  int n = atoi(strParam);

  brush_t* pCopy = NULL;
  if (selected_brushes.next != &selected_brushes && 
      selected_brushes.next->next == &selected_brushes)
    pCopy = selected_brushes.next;

  if (pCopy)
  {
    if (n == 1)
    {
      //if (g_pHold1)
        //Brush_Free(g_pHold1);
      g_pHold1 = CopyBrush(pCopy);
    }
    else if (n == 2)
    {
      //if (g_pHold2)
        //Brush_Free(g_pHold2);
      g_pHold2 = CopyBrush(pCopy);
    }
    else
    {
      //if (g_pHold3)
        //Brush_Free(g_pHold3);
      g_pHold3 = CopyBrush(pCopy);
    }
  }
}

void MoveSelected(char*& pBuffer)
{
  vec3_t v;
  CString strParam = GetParam(pBuffer);
  v[0] = atof(strParam);
  strParam = GetParam(pBuffer);
  v[1] = atof(strParam);
  strParam = GetParam(pBuffer);
  v[2] = atof(strParam);
  Select_Move(v, false);
  Sys_UpdateWindows(W_ALL);
}

void RotateSelected(char*& pBuffer)
{
  vec3_t v;

  if (g_bRotateAroundSelection)
  {
    Select_GetTrueMid(v);
    VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin());
  }

  CString strParam = GetParam(pBuffer);
  v[0] = atof(strParam);
  strParam = GetParam(pBuffer);
  v[1] = atof(strParam);
  strParam = GetParam(pBuffer);
  v[2] = atof(strParam);
  for (int i = 0; i < 3; i++)
    if (v[i] != 0.0)
      Select_RotateAxis(i, v[i], false , true);
  Sys_UpdateWindows(W_ALL);
}

void MoveHold(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  brush_t* pBrush = NULL;
  int nHold = atoi(strParam);
  if (nHold == 1)
    pBrush = g_pHold1;
  else if (nHold == 2)
    pBrush = g_pHold2;
  else 
    pBrush = g_pHold3;

  if (pBrush)
  {
    vec3_t v;
    strParam = GetParam(pBuffer);
    v[0] = atof(strParam);
    strParam = GetParam(pBuffer);
    v[1] = atof(strParam);
    strParam = GetParam(pBuffer);
    v[2] = atof(strParam);
		Brush_Move (pBrush, v, false);
  }
}

void RotateHold(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  brush_t* pBrush = NULL;
  int nHold = atoi(strParam);
  if (nHold == 1)
    pBrush = g_pHold1;
  else if (nHold == 2)
    pBrush = g_pHold2;
  else 
    pBrush = g_pHold3;

  if (pBrush)
  {
    vec3_t v;
    strParam = GetParam(pBuffer);
    v[0] = atof(strParam);
    strParam = GetParam(pBuffer);
    v[1] = atof(strParam);
    strParam = GetParam(pBuffer);
    v[2] = atof(strParam);
    for (int i = 0; i < 3; i++)
      if (v[i] != 0.0)
        Select_RotateAxis(i, v[i]);
  }
}

void CopyToMap(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  brush_t* pBrush = NULL;
  int nHold = atoi(strParam);
  if (nHold == 1)
    pBrush = g_pHold1;
  else if (nHold == 2)
    pBrush = g_pHold2;
  else 
    pBrush = g_pHold3;

  if (pBrush)
  {
    Brush_AddToList(pBrush, &active_brushes);
		Entity_LinkBrush (world_entity, pBrush);
		Brush_Build(pBrush, false);
		
    Sys_UpdateWindows(W_ALL);
  }
}

void CopyAndSelect(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  brush_t* pBrush = NULL;
  int nHold = atoi(strParam);
  if (nHold == 1)
    pBrush = g_pHold1;
  else if (nHold == 2)
    pBrush = g_pHold2;
  else 
    pBrush = g_pHold3;

  if (pBrush)
  {
    Select_Deselect();
    Brush_AddToList(pBrush, &active_brushes);
		Entity_LinkBrush (world_entity, pBrush);
		Brush_Build(pBrush, false);

	Select_Brush(pBrush);
    Sys_UpdateWindows(W_ALL);
  }
}

void Input(char*& pBuffer)
{
  bool bGo = false;
  const char *fields[5] = { "", "", "", "", "" };
  float values[5];

  for (int n = 0; n < g_nVariableCount; n++)
  {
    if (g_Variables[n].m_strInput.GetLength() > 0)
    {
      bGo = true;
      if (n < 5)
      {
        switch (n)
        {
          case 0 : fields[1] = g_Variables[n].m_strInput.GetBuffer (); break;
          case 1 : fields[2] = g_Variables[n].m_strInput.GetBuffer (); break;
          case 2 : fields[3] = g_Variables[n].m_strInput.GetBuffer (); break;
          case 3 : fields[4] = g_Variables[n].m_strInput.GetBuffer (); break;
          case 4 : fields[5] = g_Variables[n].m_strInput.GetBuffer (); break;
        }
      }
    }
  }

  if (bGo)
  {
    if (DoBSInputDlg (fields, values) == IDOK)
    {
      for (int n = 0; n < g_nVariableCount; n++)
      {
        if (g_Variables[n].m_strInput.GetLength() > 0)
        {
          if (n < 5)
          {
            switch (n)
            {
              case 0 : g_Variables[n].m_fValue = values[1]; break;
              case 1 : g_Variables[n].m_fValue = values[2]; break;
              case 2 : g_Variables[n].m_fValue = values[3]; break;
              case 3 : g_Variables[n].m_fValue = values[4]; break;
              case 4 : g_Variables[n].m_fValue = values[5]; break;
            }
          }
        }
      }
    }
    else g_bKeepGoing = false;
  }
}

bool g_bWaiting;
void _3DPointDone(bool b, int n)
{
  g_bWaiting = false;
}

void _3DPointInput(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  CString strParam2 = GetParam(pBuffer);
  ShowInfoDialog(strParam2);
  AddVectorVariable(strParam, strParam2);
  g_bWaiting = true;
  AcquirePath(2, &_3DPointDone);
  while (g_bWaiting)
    gtk_main_iteration ();
  HideInfoDialog();
  SetVectorVariableValue(strParam, g_PathPoints[0]);
}

void SetRotateOrigin(char*& pBuffer)
{
  vec3_t v;
  CString strParam = GetParam(pBuffer);
  VectorVariableValue(strParam, v);
  VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin());
  g_bRotateAroundSelection = false;
}

void InputVar(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  CString strParam2 = GetParam(pBuffer);
  AddVariable(strParam, 0.0, strParam2);
}

void LoopCount(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  g_nLoopCounter = atoi(strParam);
  if (g_nLoopCounter == 0)
    g_nLoopCounter = (int)VariableValue(strParam);
  if (g_nLoopCounter > 0)
    g_pLooper = pBuffer;
}

void LoopRun(char*& pBuffer)
{
  if (g_bStartLoop == true)
  {
    g_nLoopCounter--;
    if (g_nLoopCounter == 0)
    {
      g_bStartLoop = false;
      GetParam(pBuffer);
    }
    else
      pBuffer = g_pLooper;
  }
  else
  {
    if (g_pLooper && g_nLoopCounter > 0)
    {
      g_bStartLoop = true;
      pBuffer = g_pLooper;
    }
    else
    {
      GetParam(pBuffer);
    }
  }
}


void ConfirmMessage(char*& pBuffer)
{
  CString strParam = GetParam(pBuffer);
  if (gtk_MessageBox(g_pParentWnd->m_pWidget, strParam, "Script Info", MB_OKCANCEL) == IDCANCEL)
    g_bKeepGoing = false;
}

void Spherize(char*& pBuffer)
{
  g_bScreenUpdates = false;
  for (int n = 0; n < 120; n += 36)
  {
    for (int i = 0; i < 360; i += 36)
    {
      Select_RotateAxis(0, i, false , true);
      CSG_Subtract();
    }
    Select_RotateAxis(2, n, false , true);
  }
  g_bScreenUpdates = true;
}

void RunIt(char*& pBuffer);
SBrushScript g_ScriptCmds[] =
{
  {"_CopySelected", &CopySelected},
  {"_MoveSelected", &MoveSelected},
  {"_RotateSelected", &RotateSelected},
  {"_MoveHold", &MoveHold},
  {"_RotateHold", &RotateHold},
  {"_CopyToMap", &CopyToMap},
  {"_CopyAndSelect", &CopyAndSelect},
  {"_Input", &Input},
  {"_3DPointInput", &_3DPointInput},
  {"_SetRotateOrigin", &SetRotateOrigin},
  {"_InputVar", &InputVar},
  {"_LoopCount", &LoopCount},
  {"_LoopRun", &LoopRun},
  {"_ConfirmMessage", &ConfirmMessage},
  {"_Spherize", &Spherize},
  {"_RunScript", RunIt}
};

const int g_nScriptCmdCount = sizeof(g_ScriptCmds) / sizeof(SBrushScript);

void RunScript(char* pBuffer)
{
  g_pHold1 = NULL;
  g_pHold2 = NULL;
  g_pHold3 = NULL;

  while (g_bKeepGoing && pBuffer && *pBuffer)
  {
    while (*pBuffer != (char)NULL && *pBuffer != '_')
      pBuffer++;

    char* pTemp = pBuffer;
    int nLen = 0;
    while (*pTemp != (char)NULL && *pTemp != '(')
    {
      pTemp++;
      nLen++;
    }
    if (*pBuffer != (char)NULL)
    {
      bool bFound = false;
      for (int i = 0; i < g_nScriptCmdCount; i++)
      {
        //if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, strlen(g_ScriptCmds[i].m_pName)) ==  0)
        if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, nLen) ==  0)
        {
          pBuffer += strlen(g_ScriptCmds[i].m_pName);
          g_ScriptCmds[i].m_pProc(pBuffer);
          if (g_bStartLoop)
          {
          }
          bFound = true;
          break;
        }
      }
      if (!bFound)
        pBuffer++;
    }
  }
}


void RunScriptByName(char* pBuffer, bool bInit)
{
  if (bInit)
    InitForScriptRun();
  char* pScript = new char[4096];
  CString strINI;
  strINI = g_strGameToolsPath;
  strINI += "/scripts.ini";
  CString strScript;
  FILE *f;

  f = fopen (strINI.GetBuffer(), "rt");
  if (f != NULL)
  {
    char line[1024], *ptr;

    // read section names
    while (fgets (line, 1024, f) != 0)
    {
      if (line[0] != '[')
	continue;

      ptr = strchr (line, ']');
      *ptr = '\0';

      if (strcmp (line, pScript) == 0)
      {
	while (fgets (line, 1024, f) != 0)
	{
	  if ((strchr (line, '=') == NULL) ||
	      strlen (line) == 0)
	    break;
	  strScript += line;
	}
	break;
      }
    }
    fclose (f);
  }
  RunScript((char*)strScript.GetBuffer());
}


void RunIt(char*& pBuffer)
{
  brush_t* p1 = g_pHold1;
  brush_t* p2 = g_pHold2;
  brush_t* p3 = g_pHold3;

  CString strParam = GetParam(pBuffer);
  RunScriptByName((char*)strParam.GetBuffer(), false);

  g_pHold3 = p3;
  g_pHold2 = p2;
  g_pHold1 = p1;
}

