#ifndef TWEAKABLE_H
#define TWEAKABLE_H


#include "gtArraySimple.h"
#include <stdint.h>
#include "MiscHelpers.h"
#include "gtVector3i.h"


// Define an interface for variables which can be tweaked
class Tweakable
{
public:
  virtual ~Tweakable() {}
  virtual const char* GetName() const = 0;
  virtual const char* GetValue() const = 0;
  virtual void Increment() = 0;
  virtual void Decrement() = 0;
  virtual void Select() = 0;      // possible to do some action if enter pressed
  virtual bool AutoRepeat() = 0;
};


// Little helper to convert different types to a const char*
class String
{
public:
  String(char a_c)             { m_str = new char[64]; SNPRINTF(m_str, 64, "%c",   a_c); }
  String(const char* a_s)      { m_str = new char[64]; SNPRINTF(m_str, 64, "%s",   a_s); }
  String(int a_i)              { m_str = new char[64]; SNPRINTF(m_str, 64, "%i",   a_i); }
  String(unsigned a_i)         { m_str = new char[64]; SNPRINTF(m_str, 64, "%ui",  a_i); }
  String(float a_f)            { m_str = new char[64]; SNPRINTF(m_str, 64, "%.3g", a_f); }
  String(double a_f)           { m_str = new char[64]; SNPRINTF(m_str, 64, "%.3g", a_f); }
  String(bool a_b)             { m_str = new char[64]; SNPRINTF(m_str, 64, "%s", (a_b) ? "true" : "false"); }
  String(gtVector3i a_v)       { m_str = new char[64]; SNPRINTF(m_str, 64, "( %i, %i, %i )", a_v.x, a_v.y, a_v.z); }

  operator const char*()       { return m_str; }
  static void Release(const char* a_str) { delete[] a_str; }
private:
  char* m_str; // for small strings
};


// Generic tweakable implementation which uses the String class above for automatic conversion of the variable to a string for printing
// To handle new types using this generic implementation, one must implement an operator/constructor to convert the variable type to a String object
template <typename T>
class TweakableT : public Tweakable
{
public:
  TweakableT(const char* a_name, T& a_var, const T a_inc) : m_name(a_name), m_var(a_var), m_inc(a_inc) {}
  virtual const char* GetName() const   { return m_name; }
  virtual const char* GetValue() const  { return (const char*)String(m_var); }
  virtual void Increment()              { m_var += m_inc; }
  virtual void Decrement()              { m_var -= m_inc; }
  virtual void Select()                 { }
  virtual bool AutoRepeat()             { return false; }
private:
  const char* m_name;
  T& m_var;
  const T m_inc;
};


// Specializations for the bool case
void TweakableT<bool>::Select()      {  m_var = (m_var) ? false : true; }
void TweakableT<bool>::Increment()   {  m_var = (m_var) ? false : true; }
void TweakableT<bool>::Decrement()   {  m_var = (m_var) ? false : true; }
bool TweakableT<bool>::AutoRepeat()  {  return false; }


// A static list of const char * strings in an array can be rotated through with this tweakable implmentation
class StringListTweakable : public Tweakable
{
public:
  StringListTweakable(const char* a_name, const char** a_list, int a_elements, int& a_index) : m_name(a_name), m_StringList(a_list), m_stringListSize(a_elements), m_stringListIndex(a_index) {}
  ~StringListTweakable() {}
  const char* GetName() const   { return m_name; }
  const char* GetValue() const  { return DuplicateString((m_stringListIndex > 0 && m_stringListIndex < m_stringListSize) ? m_StringList[m_stringListIndex] : "<invalid index>"); };
  void Increment()              { m_stringListIndex = (m_stringListIndex == m_stringListSize - 1) ? 0 : m_stringListIndex + 1; }
  void Decrement()              { m_stringListIndex = (m_stringListIndex == 0) ? m_stringListSize - 1 : m_stringListIndex - 1; }
  void Select()                 { }
  bool AutoRepeat()             { return false; }
private:
  const char* m_name;
  const char** m_StringList;
  int m_stringListSize;
  int& m_stringListIndex;
};


// Convenience macro for adding Tweakable variables
#define NEW_TWEAKABLE(var, inc)         (new TweakableT<decltype(var)>(#var, var, inc))
#define NEW_LIST_TWEAKABLE(var, index)  (new StringListTweakable(#var, var, ARRAYSIZE(var), index))


// Manage a list of tweakables, printing and updating the input when called at appropriate times
class Tweakables
{
public:
  Tweakables();
  virtual ~Tweakables();

  void Add(Tweakable* a_tweakable);
  Tweakable* FindByName(const char* a_tweakableName);

  void Print(float x, float y, float w, float h);
  void UpdateInput();

private:
  uint32_t m_currentTweakable;
  gtArraySimple<Tweakable*> m_tweakables;
};


#endif // TWEAKABLE_H
