

#include "tunit.h"
#include "tconvert.h"
#include <math.h>

// Note: convertTo() means: convert from standard unit TO specific unit
// e.g. if unit=cm (standard=inch) then convertTo(1) == 2.54
//-------------------------------------------------------------------

/*
class VerticalCameraFldUnitConverter final : public TUnitConverter {
public:
  static double m_factor;
  VerticalCameraFldUnitConverter() {}
  TUnitConverter * clone() const {return new
VerticalCameraFldUnitConverter(*this);}
  double convertTo(double v) const {return v*m_factor;}
  double convertFrom(double v) const {return v/m_factor;}
};

double VerticalCameraFldUnitConverter::m_factor = 1;
*/

namespace UnitParameters {

static std::pair<double, double> dummyCurrentDpiGetter() {
  return std::make_pair<double, double>(72, 72);
}

CurrentDpiGetter currentDpiGetter = &dummyCurrentDpiGetter;

void setCurrentDpiGetter(CurrentDpiGetter f) { currentDpiGetter = f; }
}  // namespace UnitParameters

//-------------------------------------------------------------------

class VerticalFldUnitConverter final : public TUnitConverter {
  double m_factor;

public:
  static double m_fieldGuideAspectRatio;

  VerticalFldUnitConverter(double factor) : m_factor(factor) {}
  TUnitConverter *clone() const override {
    return new VerticalFldUnitConverter(*this);
  }
  double convertTo(double v) const override {
    return v * m_fieldGuideAspectRatio * m_factor;
  }
  double convertFrom(double v) const override {
    return v / (m_fieldGuideAspectRatio * m_factor);
  }
};
double VerticalFldUnitConverter::m_fieldGuideAspectRatio = 1.38;

//-------------------------------------------------------------------

namespace UnitParameters {

void setFieldGuideAspectRatio(double ar) {
  assert(ar > 0);
  VerticalFldUnitConverter::m_fieldGuideAspectRatio = ar;
}

double getFieldGuideAspectRatio() {
  return VerticalFldUnitConverter::m_fieldGuideAspectRatio;
}
}  // namespace UnitParameters

//===================================================================

class TangentConverter final : public TUnitConverter {
public:
  TangentConverter() {}
  TUnitConverter *clone() const override { return new TangentConverter(*this); }
  double convertTo(double v) const override {
    return atan(v) * (M_1_PI * 180.0);
  }
  double convertFrom(double v) const override { return tan(v * M_PI_180); }
};

//===================================================================

class TPixelUnitXConverter final : public TUnitConverter {
public:
  TPixelUnitXConverter() {}
  TUnitConverter *clone() const override {
    return new TPixelUnitXConverter(*this);
  }
  double convertTo(double v) const override {
    return v * UnitParameters::currentDpiGetter().first;
  }
  double convertFrom(double v) const override {
    return v / UnitParameters::currentDpiGetter().first;
  }
};

class TPixelUnitYConverter final : public TUnitConverter {
public:
  TPixelUnitYConverter() {}
  TUnitConverter *clone() const override {
    return new TPixelUnitYConverter(*this);
  }
  double convertTo(double v) const override {
    return v * UnitParameters::currentDpiGetter().second;
  }
  double convertFrom(double v) const override {
    return v / UnitParameters::currentDpiGetter().second;
  }
};

//===================================================================

TUnit::TUnit(std::wstring ext, TUnitConverter *converter)
    : m_defaultExtension(ext), m_converter(converter) {
  m_extensions.push_back(ext);
  if (m_converter == 0) m_converter = new TSimpleUnitConverter();
}

//-------------------------------------------------------------------

TUnit::TUnit(const TUnit &src)
    : m_defaultExtension(src.m_defaultExtension)
    , m_extensions(src.m_extensions)
    , m_converter(src.m_converter->clone()) {}

//-------------------------------------------------------------------

TUnit::~TUnit() { delete m_converter; }

//-------------------------------------------------------------------

void TUnit::addExtension(std::wstring ext) {
  if (std::find(m_extensions.begin(), m_extensions.end(), ext) ==
      m_extensions.end())
    m_extensions.push_back(ext);
  if (m_defaultExtension.empty()) m_defaultExtension = ext;
}

//-------------------------------------------------------------------

bool TUnit::isExtension(std::wstring ext) const {
  return std::find(m_extensions.begin(), m_extensions.end(), ext) !=
         m_extensions.end();
}

//-------------------------------------------------------------------

void TUnit::setDefaultExtension(std::wstring ext) {
  if (!ext.empty() && std::find(m_extensions.begin(), m_extensions.end(),
                                ext) == m_extensions.end())
    m_extensions.push_back(ext);
  m_defaultExtension = ext;
}

//===================================================================

TMeasure::TMeasure(std::string name, TUnit *mainUnit)
    : m_name(name)
    , m_mainUnit(0)
    , m_standardUnit(0)
    , m_currentUnit(0)
    , m_defaultValue(0) {
  add(mainUnit);
  m_mainUnit = m_currentUnit = m_standardUnit = mainUnit;
}

//-------------------------------------------------------------------

TMeasure::TMeasure(const TMeasure &src)
    : m_name(src.m_name)
    , m_mainUnit(src.m_mainUnit)
    , m_currentUnit(src.m_currentUnit)
    , m_standardUnit(src.m_standardUnit)
    , m_defaultValue(src.m_defaultValue) {
  std::map<std::wstring, TUnit *>::const_iterator it;
  for (it = src.m_extensions.begin(); it != src.m_extensions.end(); ++it) {
    TUnit *u = it->second;
    assert(u);
    const std::vector<std::wstring> &e = u->getExtensions();
    assert(std::find(e.begin(), e.end(), it->first) != e.end());
    m_extensions[it->first] = u;
  }
}

//-------------------------------------------------------------------

TMeasure::~TMeasure() {}

//-------------------------------------------------------------------

void TMeasure::add(TUnit *unit) {
  const std::vector<std::wstring> &e = unit->getExtensions();
  for (int i = 0; i < (int)e.size(); i++) {
    std::wstring ext = e[i];
    assert(m_extensions.count(ext) == 0);
    m_extensions[ext] = unit;
  }
}

//-------------------------------------------------------------------

TUnit *TMeasure::getUnit(std::wstring ext) const {
  std::map<std::wstring, TUnit *>::const_iterator it;
  it = m_extensions.find(ext);
  return it == m_extensions.end() ? 0 : it->second;
}

//-------------------------------------------------------------------

void TMeasure::setCurrentUnit(TUnit *unit) {
  assert(unit);
  assert(m_extensions.count(unit->getDefaultExtension()) > 0);
  m_currentUnit = unit;
}

//-------------------------------------------------------------------

void TMeasure::setStandardUnit(TUnit *unit) {
  assert(unit);
  assert(m_extensions.count(unit->getDefaultExtension()) > 0);
  m_standardUnit = unit;
}

//===================================================================

TMeasureManager::TMeasureManager() {
  TUnit inch(L"in"), cm(L"cm", new TSimpleUnitConverter(2.54)),
      mm(L"mm", new TSimpleUnitConverter(25.4)),

      xfld(L"fld", new TSimpleUnitConverter(2)),
      cameraXFld(L"fld", new TSimpleUnitConverter(1)),

      yfld(L"fld", new VerticalFldUnitConverter(2)),
      cameraYFld(L"fld", new VerticalFldUnitConverter(1)),

      levelXFld(L"fld", new TSimpleUnitConverter(1)),
      levelYFld(L"fld", new VerticalFldUnitConverter(1)),

      internalZDepth(L"internal.zdepth"),
      zdepth(L"zdepth", new TSimpleUnitConverter(1)), degree(L"\u00b0"),
      scale(L"*"), percentage2(L"%"), shear(L"sh"),
      shearAngle(L"\u00b0", new TangentConverter()),
      percentage(L"%", new TSimpleUnitConverter(100)),
      colorChannel(L"", new TSimpleUnitConverter(255)), dummy(L""),
      xPixel(L"px", new TPixelUnitXConverter()),
      yPixel(L"px", new TPixelUnitYConverter());

  inch.addExtension(L"inch");
  inch.addExtension(L"\"");
  inch.addExtension(L"''");
  inch.setDefaultExtension(L"\"");

  xPixel.addExtension(L"pixel");
  yPixel.addExtension(L"pixel");

  xfld.addExtension(L"field");
  cameraXFld.addExtension(L"field");

  yfld.addExtension(L"field");
  cameraYFld.addExtension(L"field");

  levelXFld.addExtension(L"field");
  levelYFld.addExtension(L"field");

  xfld.addExtension(L"F");
  yfld.addExtension(L"F");
  cameraXFld.addExtension(L"F");
  cameraYFld.addExtension(L"F");

  levelXFld.addExtension(L"F");
  levelYFld.addExtension(L"F");

  TMeasure *length, *m;

  length = m = new TMeasure("length", inch.clone());
  m->add(cm.clone());
  /*---
  The dimensional parameters of Fx are unitless (actually it uses the unit of
  StageInch (1 StageInch = 1/53.33333 inch)). If you refer to a parameter with a
  unit in Expression from the Fx dimension parameter, the Fx calculation results
  will vary depending on the current Unit. Since the current Unit used in
  tcomposer is the default value, we can set the default current Unit to mm here
  so that if we create a scene with Unit = mm, we will get the same Render
  results as when we work.
  ---*/
  TUnit *mmUnit = mm.clone();
  m->add(mmUnit);
  m->setCurrentUnit(mmUnit);
  add(m);

  m = new TMeasure(*length);
  m->setName("length.x");
  m->add(xfld.clone());
  m->add(xPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("length.y");
  m->add(yfld.clone());
  m->add(yPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("length.lx");
  m->add(xfld.clone());
  m->add(xPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("length.ly");
  m->add(yfld.clone());
  m->add(yPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("camera.lx");
  m->add(cameraXFld.clone());
  m->add(xPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("camera.ly");
  m->add(cameraYFld.clone());
  m->add(yPixel.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("level.lx");
  m->add(levelXFld.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("level.ly");
  m->add(levelYFld.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("canvas.lx");
  m->add(cameraXFld.clone());
  add(m);

  m = new TMeasure(*length);
  m->setName("canvas.ly");
  m->add(cameraYFld.clone());
  add(m);

  const double stage_inch        = 53.33333;
  const double stage_standardDpi = 120;

  TUnit fxLength(L"fxLength"),
      fxInch(L"in", new TSimpleUnitConverter(1 / stage_inch)),
      fxCm(L"cm", new TSimpleUnitConverter(2.54 / stage_inch)),
      fxMm(L"mm", new TSimpleUnitConverter(25.4 / stage_inch)),
      fxXfld(L"fld", new TSimpleUnitConverter(2 / stage_inch)),
      fxPx(L"px", new TSimpleUnitConverter(stage_standardDpi / stage_inch));
  fxInch.addExtension(L"inch");
  fxInch.addExtension(L"\"");
  fxInch.addExtension(L"''");
  fxInch.setDefaultExtension(L"\"");
  fxXfld.addExtension(L"field");
  fxXfld.addExtension(L"F");
  fxPx.addExtension(L"pixel");
  m = new TMeasure("fxLength", fxLength.clone());
  m->add(fxInch.clone());
  m->add(fxCm.clone());
  m->add(fxMm.clone());
  m->add(fxXfld.clone());
  m->add(fxPx.clone());
  add(m);

  m = new TMeasure("angle", degree.clone());
  add(m);

  m           = new TMeasure("scale", scale.clone());
  TUnit *unit = percentage.clone();
  m->add(unit);
  m->setCurrentUnit(unit);
  m->setStandardUnit(unit);
  add(m);

  m    = new TMeasure("percentage", scale.clone());
  unit = percentage.clone();
  m->add(unit);
  m->setCurrentUnit(unit);
  m->setStandardUnit(unit);
  add(m);

  m = new TMeasure("percentage2", percentage2.clone());
  add(m);

  m    = new TMeasure("shear", shear.clone());
  unit = shearAngle.clone();
  m->add(unit);
  m->setCurrentUnit(unit);
  m->setStandardUnit(unit);
  add(m);

  m = new TMeasure("colorChannel", colorChannel.clone());
  add(m);

  /*
m = new TMeasure("zdepth", internalZDepth.clone());
unit = zdepth.clone();
m->add(unit);
m->setCurrentUnit(unit);
m->setStandardUnit(unit);
add(m);
*/
  m = new TMeasure("dummy", dummy.clone());
  add(m);
}

//-------------------------------------------------------------------

void TMeasureManager::add(TMeasure *m) { m_measures[m->getName()] = m; }

//-------------------------------------------------------------------

TMeasure *TMeasureManager::get(std::string name) const {
  std::map<std::string, TMeasure *>::const_iterator it;
  it = m_measures.find(name);
  if (it == m_measures.end())
    return 0;
  else
    return it->second;
}

//===================================================================

TMeasuredValue::TMeasuredValue(std::string measureName)
    : m_measure(0), m_value(0) {
  setMeasure(measureName);
}

//-------------------------------------------------------------------

TMeasuredValue::~TMeasuredValue() {}

//-------------------------------------------------------------------

void TMeasuredValue::setMeasure(const TMeasure *measure) {
  assert(measure);
  if (!measure) return;
  m_measure = measure;
  m_value   = m_measure->getDefaultValue();
}

//-------------------------------------------------------------------

void TMeasuredValue::setMeasure(std::string measureName) {
  setMeasure(TMeasureManager::instance()->get(measureName));
}

//-------------------------------------------------------------------

bool TMeasuredValue::setValue(std::wstring s, int *pErr) {
  if (s == L"" || s == L"-") {
    if (pErr) *pErr = -1;
    return false;
  }
  const TUnit *unit = m_measure->getCurrentUnit();
  double value      = 0;
  bool valueFlag    = false;
  int i = 0, len = s.length();
  // skip blanks
  i = s.find_first_not_of(::to_wstring(" \t"));
  assert(i != (int)std::wstring::npos);
  int j = i;
  // match number
  if (i < len && (s[i] == L'-' || s[i] == L'+')) i++;
  while (i < len && L'0' <= s[i] && s[i] <= L'9') i++;
  if (i < len && s[i] == L'.') {
    i++;
    while (i < len && L'0' <= s[i] && s[i] <= L'9') i++;
    if (i < len && (s[i] == L'E' || s[i] == L'e')) {
      i++;
      if (i < len && (s[i] == L'-' || s[i] == L'+')) i++;
      while (i < len && L'0' <= s[i] && s[i] <= L'9') i++;
    }
  }
  if (i > j) {
    try {
      value = std::stod(s.substr(j, i - j));
    }
    // handle exceptions
    catch (const std::invalid_argument &) {
      return false;
    } catch (const std::out_of_range &) {
      return false;
    }

    valueFlag = true;
    // skip blanks
    i = s.find_first_not_of(::to_wstring(" \t"), i);
    if (i == (int)std::wstring::npos) i = s.length();
  }

  // remove trailing blanks
  if (i < (int)s.length()) {
    j = i;
    i = s.find_last_not_of(::to_wstring(" \t"));
    if (i == (int)std::wstring::npos) i = len - 1;
    if (j <= i) {
      std::wstring unitExt = s.substr(j, i + 1 - j);
      unit                 = m_measure->getUnit(unitExt);
      if (!unit) {
        if (pErr) *pErr = -2;
        return false;
      }
    }
  }

  if (valueFlag) {
    double newValue = unit->convertFrom(value);
    if (m_value == newValue) {
      if (pErr) *pErr = 0;
      return false;
    }
    m_value = newValue;
  }
  if (pErr) *pErr = (valueFlag || unit != 0) ? 0 : -3;
  return valueFlag || unit != 0;
}

//-------------------------------------------------------------------

std::wstring TMeasuredValue::toWideString(int decimals) const {
  double v      = getValue(CurrentUnit);
  std::string s = ::to_string(v, decimals);
  if (s.find('.') != std::string::npos) {
    int i = s.length();
    while (i > 0 && s[i - 1] == '0') i--;
    if (i > 0 && s[i - 1] == '.') i--;
    if (i < (int)s.length()) s = s.substr(0, i);
  }
  std::wstring measure = m_measure->getCurrentUnit()->getDefaultExtension();
  if (measure.empty()) return ::to_wstring(s);
  return ::to_wstring(s) + L" " + measure;
}

//-------------------------------------------------------------------

void TMeasuredValue::modifyValue(double direction, int precision) {
  for (int i = 0; i < precision; i++) {
    direction /= 10;
  }
  std::wstring currExtension =
      getMeasure()->getCurrentUnit()->getDefaultExtension();
  // use a smaller value for inches and cm
  if (currExtension == L"\"" || currExtension == L"cm") direction /= 10;
  double v = getValue(CurrentUnit);
  setValue(CurrentUnit, v + direction);
}

//===================================================================

namespace {

class ZDepthUnitConverter final : public TUnitConverter {
  TMeasureManager::CameraSizeProvider *m_cameraSizeProvider;

public:
  ZDepthUnitConverter(TMeasureManager::CameraSizeProvider *cameraSizeProvider)
      : m_cameraSizeProvider(cameraSizeProvider) {}
  TUnitConverter *clone() const override {
    return new ZDepthUnitConverter(m_cameraSizeProvider);
  }
  inline double getCameraSize() const { return (*m_cameraSizeProvider)(); }
  double convertTo(double v) const override {
    return (1 - v * 0.001) * getCameraSize();
  }
  double convertFrom(double v) const override {
    return (1 - v / getCameraSize()) * 1000.0;
  }
};

//-------------------------------------------------------------------

class CameraZDepthUnitConverter final : public TUnitConverter {
  TMeasureManager::CameraSizeProvider *m_cameraSizeProvider;

public:
  CameraZDepthUnitConverter(
      TMeasureManager::CameraSizeProvider *cameraSizeProvider)
      : m_cameraSizeProvider(cameraSizeProvider) {}
  TUnitConverter *clone() const override {
    return new CameraZDepthUnitConverter(m_cameraSizeProvider);
  }
  inline double getCameraSize() const { return (*m_cameraSizeProvider)(); }
  double convertTo(double v) const override {
    return (1 + v * 0.001) * getCameraSize();
  }
  double convertFrom(double v) const override {
    return (v / getCameraSize() - 1) * 1000.0;
  }
};

//===================================================================
/*-- The length of the Z curve handle must be 0=0 --*/
class ZDepthHandleUnitConverter final : public TUnitConverter {
  TMeasureManager::CameraSizeProvider *m_cameraSizeProvider;

public:
  ZDepthHandleUnitConverter(
      TMeasureManager::CameraSizeProvider *cameraSizeProvider)
      : m_cameraSizeProvider(cameraSizeProvider) {}

  TUnitConverter *clone() const override {
    return new ZDepthHandleUnitConverter(m_cameraSizeProvider);
  }
  inline double getCameraSize() const { return (*m_cameraSizeProvider)(); }
  double convertTo(double v) const override {
    return -v * 0.001 * getCameraSize();
  }
  double convertFrom(double v) const override {
    return (-v / getCameraSize()) * 1000.0;
  }
};

class CameraZDepthHandleUnitConverter final : public TUnitConverter {
  TMeasureManager::CameraSizeProvider *m_cameraSizeProvider;

public:
  CameraZDepthHandleUnitConverter(
      TMeasureManager::CameraSizeProvider *cameraSizeProvider)
      : m_cameraSizeProvider(cameraSizeProvider) {}

  TUnitConverter *clone() const override {
    return new CameraZDepthHandleUnitConverter(m_cameraSizeProvider);
  }
  inline double getCameraSize() const { return (*m_cameraSizeProvider)(); }
  double convertTo(double v) const override {
    return v * 0.001 * getCameraSize();
  }
  double convertFrom(double v) const override {
    return (v / getCameraSize()) * 1000.0;
  }
};

}  // namespace

//-------------------------------------------------------------------

void TMeasureManager::addCameraMeasures(
    CameraSizeProvider *cameraSizeProvider) {
  TUnit u0(L"z"), u1(L"fld", new ZDepthUnitConverter(cameraSizeProvider)),
      u2(L"fld", new CameraZDepthUnitConverter(cameraSizeProvider)),
      u3(L"fld", new ZDepthHandleUnitConverter(cameraSizeProvider)),
      u4(L"fld", new CameraZDepthHandleUnitConverter(cameraSizeProvider));

  TMeasure *zdepth = new TMeasure("zdepth", u0.clone());
  TUnit *u         = u1.clone();
  zdepth->add(u);
  zdepth->setCurrentUnit(u);
  zdepth->setStandardUnit(u);
  TMeasureManager::instance()->add(zdepth);

  zdepth = new TMeasure("zdepth.cam", u0.clone());
  u      = u2.clone();
  zdepth->add(u);
  zdepth->setCurrentUnit(u);
  zdepth->setStandardUnit(u);

  TMeasureManager::instance()->add(zdepth);

  zdepth = new TMeasure("zdepth.handle", u0.clone());
  u      = u3.clone();
  zdepth->add(u);
  zdepth->setCurrentUnit(u);
  zdepth->setStandardUnit(u);
  TMeasureManager::instance()->add(zdepth);

  zdepth = new TMeasure("zdepth.cam.handle", u0.clone());
  u      = u4.clone();
  zdepth->add(u);
  zdepth->setCurrentUnit(u);
  zdepth->setStandardUnit(u);
  TMeasureManager::instance()->add(zdepth);
}
