//-------------------------------------------------------------------
//
//  This code is copyright 2001 by G5 Software.
//  Any unauthorized usage, either in part or in whole of this code
//  is strictly prohibited. Violators WILL be prosecuted to the
//  maximum extent allowed by law.
//
//-------------------------------------------------------------------

class CBaseMission extends CStrings, CMissionMessageColors
{
  //
  //  settings
  //

  //#TODO: set to true to see nav-point
  final boolean m_EditorMode = false;// editor mode

  //
  //  should be called by subclassing classes from their Init() method
  //

  int m_nObjects2Load;
  int m_nObjectsLoaded;

  void BaseMission_UpdateLoadProgress()
  {
    m_nObjectsLoaded = m_nObjectsLoaded + 1;
    int percent = m_nObjectsLoaded * 1000 / m_nObjects2Load;

    Core_CallFunction(
      SOID_GameMenu,
      "SendCommandToControl",
      "ID_LOAD_PROGRESS_BAR",
      "SET_PROGRESS",
      percent
    );
  }

  void BaseMission_InitMission()
  {
    m_bMissionStatusActive = false;
    m_bMissionMenuActive   = false;
    m_bMissionQuit         = false;

    array ids              = GetObjectsIDs();
    m_nObjects2Load        = 6 + ids.size() + GetAutoGeneratedUnitsQty();
    m_nObjectsLoaded       = 0;

    //
    //  Create components common to all missions
    //

    // CreateComponent(IDToRegister, ComponentID, ScriptName | FileName | "")
    BaseMission_UpdateLoadProgress();
    CreateComponent("Effects", "EffectsArray", "CEffectsArray");

    BaseMission_UpdateLoadProgress();
    CreateComponent("Sounds", "SoundsArray", "CSoundsArray");

    BaseMission_UpdateLoadProgress();
    CreateComponent("Bullets", "BulletsArray", "CBulletsArray");

    BaseMission_UpdateLoadProgress();
    CreateComponent("Explosions", "ExplosionsArray", "CExplosionsArray");

    BaseMission_UpdateLoadProgress();
    CreateComponent("RouterPrecalculatedGraph", "RouterPrecalculatedGraph", "CRouterPrecalculatedGraph");

    BaseMission_UpdateLoadProgress();
    CreateComponent("LensFlare", "LensFlare", "");
  }

  void BaseMission_CreateObjects()
  {
    //
    //  Create individual mission components
    //

    array ids     = GetObjectsIDs();
    array clss    = GetObjectsScriptClassNames();
    array cids    = GetObjectsScriptComponentIDs();
    array matrs   = GetObjectsMatrices();
    array behscrs = GetObjectsBehaviorScripts();
    array cstprms = GetObjectsCustomParameters();

    for (int i = 0; i < ids.size(); i = i + 1)
    {
      BaseMission_UpdateLoadProgress();

      // do not create nav points in non editor mode
      if ( clss[i] != "CNavPoint" || m_EditorMode)
      {
        CreateComponent( ids[i], cids[i], clss[i]);
  
        SetComponentPosition( ids[i], matrs[i]);

        if ( behscrs.type() == ST_ARRAY) // prevent failure of old style missions
        {
          if ( behscrs[i] != "")
          {
            SetBehaviorTask( ids[i], behscrs[i]);
          }
        }
      }

      if ( clss[i] == "CNavPoint")
      {
        string str_radius;
        if ( cstprms.type() == ST_ARRAY)
          str_radius = cstprms[i];
        else
          str_radius = "";

        // tell AIController about NavPoint
        Core_SendEventTo(
          "AIController",
          "OnAddNavPoint",
          ids[i],      // name of nav-point
          matrs[i],    // position of nav-point
          Core_String2Float( str_radius)); // custom params - radiuses

        Core_SendEventTo(
          ids[i],
          "OnSetCustomParameter",
          str_radius);
      }
    }
  }


  //
  //
  // *** routine functions
  //
  //

  void CreateUnitLine(
    string  _NamePrefix,
    string  _ObjectScript,
    string  _AIScript,
    int     _ObjectNum,
    vector  _LineStart,
    vector  _LineEnd)
  {
    for ( int u = 0; u < _ObjectNum; u = u + 1)
    {
      float x = Core_GetVectorX(_LineStart) + (Core_GetVectorX(_LineEnd) - Core_GetVectorX(_LineStart)) * u / _ObjectNum;
      float y = Core_GetVectorY(_LineStart) + (Core_GetVectorY(_LineEnd) - Core_GetVectorY(_LineStart)) * u / _ObjectNum;

      BaseMission_UpdateLoadProgress();
      CreateComponent( _NamePrefix + u, "GameObject", _ObjectScript);

      SetComponentPosition(
        _NamePrefix + u,
        matrix(
          1.0, 0.0, 0.0, x,
          0.0, 1.0, 0.0, y,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0));

      SetBehaviorTask( _NamePrefix + u, _AIScript);
    }
  }

  void CreateUnitLineGroup(
    string  _NamePrefix,
    array   _GroupObjectScript,
    array   _GroupAIScript,
    array   _GroupObjectNum,
    int     _ObjectNum,
    vector  _LineStart,
    vector  _LineEnd)
  {
    int GroupIndex = 0;
    int InGroupIndex = 0;

    for ( int u = 0; u < _ObjectNum; u = u + 1)
    {
      float x = Core_GetVectorX(_LineStart) + (Core_GetVectorX(_LineEnd) - Core_GetVectorX(_LineStart)) * u / _ObjectNum;
      float y = Core_GetVectorY(_LineStart) + (Core_GetVectorY(_LineEnd) - Core_GetVectorY(_LineStart)) * u / _ObjectNum;

      BaseMission_UpdateLoadProgress();
      CreateComponent( _NamePrefix + u, "GameObject", _GroupObjectScript[GroupIndex]);

      SetComponentPosition(
        _NamePrefix + u,
        matrix(
          1.0, 0.0, 0.0, x,
          0.0, 1.0, 0.0, y,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0));

      SetBehaviorTask( _NamePrefix + u, _GroupAIScript[GroupIndex]);

      InGroupIndex = InGroupIndex + 1;
      if ( InGroupIndex >= int(_GroupObjectNum[GroupIndex]))
      {
        GroupIndex = GroupIndex + 1;
        InGroupIndex = 0;
      };
    }
  }

  void CreateUnitField(
    string  _NamePrefix,
    string  _ObjectScript,
    string  _AIScript,
    int     _ObjectNum,
    vector  _Center,
    float   _Radius)
  {
    for ( int u = 0; u < _ObjectNum; u = u + 1)
    {
      float x = Core_GetVectorX(_Center) + rand( -_Radius, _Radius);
      float y = Core_GetVectorY(_Center) + rand( -_Radius, _Radius);

      BaseMission_UpdateLoadProgress();
      CreateComponent( _NamePrefix + u, "GameObject", _ObjectScript);

      SetComponentPosition(
        _NamePrefix + u,
        matrix(
          1.0, 0.0, 0.0, x,
          0.0, 1.0, 0.0, y,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0));

      SetBehaviorTask( _NamePrefix + u, _AIScript);
    }
  }



  //
  //  Game Object destroyed handler
  //

  void BaseMission_OnGameObjectDestroyed(string _id)
  {
    // "Helicopter" currently is a reserved id for a player's heli
    if (_id == "Helicopter")
    {
      BaseMission_FastQuit();
    }
  }

  //
  //  Delayed quit from mission method
  //

  boolean m_bMissionQuit;

  void BaseMission_DelayedQuit()
  {
    if (m_bMissionQuit)
      return;

    // call mission "QuitMission" handler
    OnQuitMission();

    // quit this mission in 10 seconds
    Core_ScheduleTask(
      SOID_GameController,
      OT_CallFunction,
      10.0,
      "OnQuitMission"
    );

    m_bMissionQuit = true;
  }

  void BaseMission_FastQuit()
  {
    if (m_bMissionQuit)
      return;

    // call mission "QuitMission" handler
    OnQuitMission();

    // quit this mission in 10 seconds
    Core_ScheduleTask(
      SOID_GameController,
      OT_CallFunction,
      1.5,
      "OnQuitMission"
    );

    m_bMissionQuit = true;
  }

  //
  //  Mission status screen
  //
  //  OnShowMissionStatus() is called from GameController
  //

  bool m_bMissionStatusActive = false;

  void OnShowMissionStatus()
  {
    // no mission status screen if menu is active
    if (m_bMissionMenuActive)
      return;
    if (m_bMissionHelpActive)
      return;

    if (m_bMissionStatusActive)
    {
      OnHideMissionStatus();
      return;
    }

    StoreControlsState();
    EnableControl("", false);
    EnableControl(SOID_MissionMenu, true);

    Core_SendEventTo(
      SOID_MissionMenu,
      "BaseMenu_SetMissionStatus",
      GetMissionObjectives(),
      GetMissionObjectivesStatuses(),
      GetBonusMissionObjectives(),
      GetBonusMissionObjectivesStatuses(),
      GetMissionStatistics(),
      true,   // in-game flag, notifies that mission is not yet over
              // and menu should display 'In progress' but not 'Failed'
              // for incomplete objectives
      false   // mission complete flag
      );

    Core_CallFunction(
      SOID_MissionMenu,
      "GoToSubMenu",
      "BaseMenu_ShowMissionStatus");

    PauseGame(true);

    m_bMissionStatusActive = true;
  }

  void OnHideMissionStatus()
  {
    if (!m_bMissionStatusActive)
      return;

    RestoreControlsState();
    PauseGame(false);

    // go to the root of the mission menu
    // so that user would see the root mission menu
    // when he invokes mission menu next time
    Core_CallFunction(SOID_MissionMenu, "GoToRootMenu");

    m_bMissionStatusActive = false;
  }

  void OnQuitMission()
  {
    Core_SendEventTo(
      SOID_GameController,
      "OnSetMissionResult",
      GetMissionObjectives(),
      GetMissionObjectivesStatuses(),
      GetBonusMissionObjectives(),
      GetBonusMissionObjectivesStatuses(),
      GetMissionStatistics()
      );
  }

  //
  //  Mission menu
  //
  //  OnShowMissionMenu() is called from GameController
  //

  bool m_bMissionMenuActive = false;

  void OnShowMissionMenu()
  {
    // no mission menu if mission status screen is active
    if (m_bMissionStatusActive)
      return;
    if (m_bMissionHelpActive)
      return;

    if (m_bMissionMenuActive)
    {
      OnHideMissionMenu();
      return;
    }

    StoreControlsState();
    EnableControl("", false);
    EnableControl(SOID_MissionMenu, true);
    PauseGame(true);

    m_bMissionMenuActive = true;
  }

  void OnForceMissionMenu()
  {
    if (m_bMissionMenuActive)
      return;

    OnShowMissionMenu();
  }

  void OnHideMissionMenu()
  {
    if (!m_bMissionMenuActive)
      return;

    RestoreControlsState();
    PauseGame(false);

    // the cursor might be hidden and locked by key control
    // in the settings screen -- unlock and show to return to normal state
    Core_CallFunction(SOID_MissionMenu, "ShowAndUnlockCursor");

    // go to the root of the mission menu
    // so that user would see the root mission menu
    // when he invokes mission menu next time
    Core_CallFunction(SOID_MissionMenu, "GoToRootMenu");

    m_bMissionMenuActive = false;
  }

  //
  //  Mission help screen
  //
  //  OnShowMissionHelpScreen() is called from GameController
  //

  bool m_bMissionHelpActive = false;

  void OnShowMissionHelpScreen()
  {
    // no mission status screen if menu is active
    if (m_bMissionMenuActive)
      return;
    if (m_bMissionStatusActive)
      return;

    if (m_bMissionHelpActive)
    {
      OnHideMissionHelpScreen();
      return;
    }

    StoreControlsState();
    EnableControl("", false);
    EnableControl(SOID_MissionMenu, true);

    Core_CallFunction(
      SOID_MissionMenu,
      "GoToSubMenu",
      "BaseMenu_ShowMissionHelpScreen");

    PauseGame(true);

    m_bMissionHelpActive = true;
  }

  void OnHideMissionHelpScreen()
  {
    if (!m_bMissionHelpActive)
      return;

    RestoreControlsState();
    PauseGame(false);

    // go to the root of the mission menu
    // so that user would see the root mission menu
    // when he invokes mission menu next time
    Core_CallFunction(SOID_MissionMenu, "GoToRootMenu");

    m_bMissionHelpActive = false;
  }


  //
  //  Service methods
  //

  void PauseGame(bool _pause)
  {
    Core_CallFunction(SOID_GameController, "PauseGame", _pause);
  }

  void StoreControlsState()
  {
    Core_CallFunction(SOID_GameController, "StoreControlsState");
  }

  void RestoreControlsState()
  {
    Core_CallFunction(SOID_GameController, "RestoreControlsState");
  }

  void EnableControl(string _control, bool _enable)
  {
    Core_CallFunction(SOID_GameController, "EnableControl", _control, _enable);
  }

  //
  //  Objectives

  array GetMissionObjectives()
  {
    return m_MissionObjectives;
  }

  array GetMissionObjectivesStatuses()
  {
    return m_MissionObjectivesStatuses;
  }

  void SetMissionObjectiveStatus(int _obj, string _status)
  {
    m_MissionObjectivesStatuses[_obj] = _status;
  }

  //
  //  Bonus objectives

  array GetBonusMissionObjectives()
  {
    return m_BonusMissionObjectives;
  }

  array GetBonusMissionObjectivesStatuses()
  {
    return m_BonusMissionObjectivesStatuses;
  }

  void SetBonusMissionObjectiveStatus(int _obj, string _status)
  {
    m_BonusMissionObjectivesStatuses[_obj] = _status;
  }

  //
  //  Mission statistics

  string GetMissionStatistics()
  {
    return "CBaseMission methods to override: 3";
  }

  //
  //  Mission navpoints

  array GetNavPoints()
  {
    array navpoints =
      array(
        vector(0, 0, 0),
        vector(16000, 16000, 0)
      );
    return navpoints;
  }

  array GetNavPointsDescriptions()
  {
    return m_NavigationPoints;
  }

  //
  //  Methods common to all missions
  //

  void BaseMission_CompleteObjective(int _obj)
  {
    array objectives = GetMissionObjectives();
    array statuses = GetMissionObjectivesStatuses();

    if (statuses[_obj] == str_ObjectiveComplete)
      return;

    SetMissionObjectiveStatus(_obj, str_ObjectiveComplete);
    Core_BroadcastEvent(
      "OnDisplayMessage",
      str_CompleteMessage + objectives[_obj],
      m_ObjectiveCompleteMessageColor
    );

    // check whether all base objectives are complete

    // refresh statuses
    statuses = GetMissionObjectivesStatuses();

    // mission is complete if all the main objectives are complete
    bool bMissionComplete = true;
    for (int i = 0; i < statuses.size(); i = i + 1)
    {
      bMissionComplete = bMissionComplete && (statuses[i] == str_ObjectiveComplete);
    }

    // check whether all bonus objectives are complete

    // refresh statuses
    statuses = GetBonusMissionObjectivesStatuses();

    // mission is complete if all the main objectives are complete
    bool bBonusMissionComplete = true;
    for (int i = 0; i < statuses.size(); i = i + 1)
    {
      bBonusMissionComplete = bBonusMissionComplete && (statuses[i] == str_ObjectiveComplete);
    }

    if (bMissionComplete)
    {
      // enable finish mission button in mission status screen
      Core_SendEventTo(
        SOID_MissionMenu,
        "BaseMenu_EnableFinishMission");
    }

    if (bMissionComplete && bBonusMissionComplete)
      BaseMission_DelayedQuit();

    if (bMissionComplete && !bBonusMissionComplete)
    {
      // show mission status in 5 seconds
      Core_ScheduleTask(
        SOID_MissionController,
        OT_CallFunction,
        5.0,
        "OnShowMissionStatus"
      );
    }
  }

  void BaseMission_FailObjective(int _obj)
  {
    array objectives = GetMissionObjectives();
    array statuses = GetMissionObjectivesStatuses();

    SetMissionObjectiveStatus(_obj, str_ObjectiveFailed);
    Core_BroadcastEvent(
      "OnDisplayMessage",
      str_FailedMessage + objectives[_obj],
      m_ObjectiveFailedMessageColor
    );

    // one of the base objectives failed - quit
    BaseMission_DelayedQuit();
  }

  void BaseMission_CompleteBonusObjective(int _obj)
  {
    array objectives = GetBonusMissionObjectives();
    array statuses = GetBonusMissionObjectivesStatuses();

    if (statuses[_obj] == str_ObjectiveComplete)
      return;

    SetBonusMissionObjectiveStatus(_obj, str_ObjectiveComplete);
    Core_BroadcastEvent(
      "OnDisplayMessage",
      str_CompleteMessage + objectives[_obj],
      m_ObjectiveCompleteMessageColor
    );

    // check whether all base and all bonus objectives are complete
    // quit if true

    // refresh statuses
    statuses             = GetMissionObjectivesStatuses();
    array bonus_statuses = GetBonusMissionObjectivesStatuses();

    // mission is complete if all the main objectives are complete...
    bool bMissionComplete = true;
    for (int i = 0; i < statuses.size(); i = i + 1)
    {
      bMissionComplete = bMissionComplete && (statuses[i] == str_ObjectiveComplete);
    }

    // ... and all bonus objectives are complete
    for (int i = 0; i < bonus_statuses.size(); i = i + 1)
    {
      bMissionComplete = bMissionComplete && (bonus_statuses[i] == str_ObjectiveComplete);
    }

    if (bMissionComplete)
      BaseMission_DelayedQuit();
  }

  void BaseMission_FailBonusObjective(int _obj)
  {
    array objectives = GetBonusMissionObjectives();
    array statuses = GetBonusMissionObjectivesStatuses();

    SetBonusMissionObjectiveStatus(_obj, str_ObjectiveInProgress);
    Core_BroadcastEvent(
      "OnDisplayMessage",
      str_FailedMessage + objectives[_obj],
      m_ObjectiveFailedMessageColor
    );
  }

  void DestroyGameObject(string _ObjectId)
  {
    if (_ObjectId != "Helicopter")
      DestroyComponent(_ObjectId);

    OnGameObjectDestroyed(_ObjectId);
  }
}

class CMissionMessageColors
{
  color m_ObjectiveCompleteMessageColor = color(0.0, 0.75, 0.0);
  color m_ObjectiveFailedMessageColor   = color(1.0, 0.0, 0.0);

  color m_GoodNewsColor = color(0.0, 0.75, 0.0);
  color m_BadNewsColor  = color(1.0, 0.0, 0.0);

  color m_ReminderColor = color(1.0, 1.0, 0.0);
}
