/*
 * GDevelop C++ Platform
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
 * reserved. This project is released under the MIT License.
 */

#if defined(GD_IDE_ONLY)
#include "CppCodeEvent.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/FileStream.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"
#include "GDCpp/Runtime/CommonTools.h"
#include "GDCpp/Runtime/RuntimeScene.h"

using namespace std;

void CppCodeEvent::SerializeTo(gd::SerializerElement& element) const {
  element.SetAttribute("functionToCall", functionToCall);
  element.SetAttribute("functionNameAutogenerated", functionNameAutogenerated);
  element.SetAttribute("inlineCode", inlineCode);
  element.SetAttribute("associatedGDManagedSourceFile",
                       associatedGDManagedSourceFile);

  element.SetAttribute("passSceneAsParameter", passSceneAsParameter);
  element.SetAttribute("passObjectListAsParameter", passObjectListAsParameter);
  element.SetAttribute("objectToPassAsParameter", objectToPassAsParameter);

  element.SetAttribute("codeDisplayedInEditor", codeDisplayedInEditor);
  element.SetAttribute("displayedName", displayedName);
  element.SetAttribute("lastChangeTimeStamp", (int)lastChangeTimeStamp);

  gd::SerializerElement& includesElement = element.AddChild("includes");
  includesElement.ConsiderAsArrayOf("include");
  for (std::size_t i = 0; i < includeFiles.size(); ++i)
    includesElement.AddChild("include").SetValue(includeFiles[i]);

  gd::SerializerElement& dependenciesElement = element.AddChild("dependencies");
  dependenciesElement.ConsiderAsArrayOf("dependency");
  for (std::size_t i = 0; i < dependencies.size(); ++i)
    dependenciesElement.AddChild("dependency")
        .SetAttribute("sourceFile", dependencies[i]);
}

void CppCodeEvent::UnserializeFrom(gd::Project& project,
                                   const gd::SerializerElement& element) {
  functionToCall =
      element.GetStringAttribute("functionToCall", "", "FunctionToCall");
  functionNameAutogenerated = element.GetBoolAttribute(
      "functionNameAutogenerated", false, "FunctionNameAutogenerated"),
  inlineCode = element.GetStringAttribute("inlineCode", "", "InlineCode");
  associatedGDManagedSourceFile = element.GetStringAttribute(
      "associatedGDManagedSourceFile", "", "AssociatedGDManagedSourceFile");

  passSceneAsParameter = element.GetBoolAttribute(
      "passSceneAsParameter", false, "PassSceneAsParameter");
  passObjectListAsParameter = element.GetBoolAttribute(
      "passObjectListAsParameter", false, "PassObjectListAsParameter");
  objectToPassAsParameter = element.GetStringAttribute(
      "objectToPassAsParameter", "", "ObjectToPassAsParameter");

  codeDisplayedInEditor = element.GetBoolAttribute(
      "codeDisplayedInEditor", true, "CodeDisplayedInEditor");
  displayedName =
      element.GetStringAttribute("displayedName", "", "DisplayedName");
  lastChangeTimeStamp = element.GetIntAttribute("lastChangeTimeStamp");

  includeFiles.clear();
  gd::SerializerElement& includesElement =
      element.GetChild("includes", 0, "Includes");
  includesElement.ConsiderAsArrayOf("include", "Include");
  for (std::size_t i = 0; i < includesElement.GetChildrenCount(); ++i)
    includeFiles.push_back(includesElement.GetChild(i).GetValue().GetString());

  dependencies.clear();
  gd::SerializerElement& dependenciesElement =
      element.GetChild("dependencies", 0, "Dependencies");
  dependenciesElement.ConsiderAsArrayOf("dependency", "Dependency");
  for (std::size_t i = 0; i < dependenciesElement.GetChildrenCount(); ++i)
    dependencies.push_back(
        dependenciesElement.GetChild(i).GetStringAttribute("sourceFile"));
}

void CppCodeEvent::AutogenerateFunctionName() const {
  std::shared_ptr<gd::BaseEvent> event = originalEvent.lock();
  if (!functionNameAutogenerated) return;

  if (event != std::shared_ptr<gd::BaseEvent>())
    functionToCall =
        "GDCppCode" +
        gd::String::From(event.get());  // The name refers to the original event
                                        // being in the original scene.
  else
    functionToCall =
        "GDCppCode" +
        gd::String::From(this);  // We are the original event: Create the name
                                 // using our address.
}

const gd::String& CppCodeEvent::GetFunctionToCall() const {
  if (functionNameAutogenerated && functionToCall.empty())
    AutogenerateFunctionName();

  return functionToCall;
};

/**
 * Initialize from another CppCodeEvent.
 * Used by copy ctor and assignement operator
 */
void CppCodeEvent::Init(const CppCodeEvent& event) {
  includeFiles = event.includeFiles;
  dependencies = event.dependencies;
  functionToCall = event.functionToCall;
  functionNameAutogenerated = event.functionNameAutogenerated;
  inlineCode = event.inlineCode;
  associatedGDManagedSourceFile = event.associatedGDManagedSourceFile;
  lastChangeTimeStamp = 0;

  passSceneAsParameter = event.passSceneAsParameter;
  passObjectListAsParameter = event.passObjectListAsParameter;
  objectToPassAsParameter = event.objectToPassAsParameter;

  codeDisplayedInEditor = event.codeDisplayedInEditor;
  displayedName = event.displayedName;
}

/**
 * Custom copy operator
 */
CppCodeEvent::CppCodeEvent(const CppCodeEvent& event) : BaseEvent(event) {
  Init(event);
}

/**
 * Custom assignement operator
 */
CppCodeEvent& CppCodeEvent::operator=(const CppCodeEvent& event) {
  if (this != &event) {
    BaseEvent::operator=(event);
    Init(event);
  }

  return *this;
}

CppCodeEvent::CppCodeEvent()
    : BaseEvent(),
      functionNameAutogenerated(true),
      inlineCode("scene.SetBackgroundColor(100,100,240);\n"),
      lastChangeTimeStamp(0),
      passSceneAsParameter(true),
      passObjectListAsParameter(false),
      codeDisplayedInEditor(true) {
  includeFiles.push_back("<iostream>");
  includeFiles.push_back("\"GDCpp/Runtime/CommonTools.h\"");
}

#endif
