//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#include "Stack.hpp"

#include "Information.hpp"

namespace mgx 
{
  
  // template <typename T>
  // static const T& max(const T& v1, const T& v2)
  //{
  // if(v1 > v2) return v1;
  // return v2;
  //}
  
  Stack::Stack(int id) : _main(new Store(this)), _work(new Store(this)), _current(_main), 
    _scale(Point3f(1.0, 1.0, 1.0)), _origin(), _texScale(1.0), _size(512, 512, 100), 
    _step(Point3f(1.0, 1.0, 1.0)), _frame(), _trans(), changed_frame(false), changed_trans(false), 
    _id(id), _CurrLabel(0), _showScale(false),
    _showTrans(false), _showBBox(false), _tieScales(true)
  {
    updateSizes();
  }
  
  Stack::Stack(const Stack& copy) : _main(new Store(*copy._main)), _work(new Store(*copy._work)), 
    _current(0), _scale(copy._scale), _origin(copy._origin), _texScale(copy._texScale), 
    _size(copy._size), _step(copy._step), _frame(copy._frame), _trans(copy._trans), 
    changed_frame(copy.changed_frame), changed_trans(copy.changed_trans),
    _id(-1), _CurrLabel(copy._CurrLabel),
    _showScale(copy._showScale), _showTrans(copy._showTrans),
    _showBBox(copy._showBBox), _tieScales(true)
  {
    _main->setStack(this);
    _work->setStack(this);
    if(copy._current)
      _current = (copy._current == copy._main) ? _main : _work;
    updateSizes();
  }
  
  void Stack::reset() {
    setSize(Point3u());
  }
  
  Stack::~Stack()
  {
    if(_main)
      delete _main;
    _main = 0;
    if(_work)
      delete _work;
    _work = 0;
    _current = 0;
  }
  
  void Stack::resetCurrent()
  {
    if(_work and _work->isVisible())
      _current = _work;
    else if(_main and _main->isVisible())
      _current = _main;
    else
      _current = 0;
  }
  
  void Stack::setMain(Store* m)
  {
    _main = m;
    resetCurrent();
  }
  
  void Stack::setWork(Store* w)
  {
    _work = w;
    resetCurrent();
  }
  
  void Stack::updateSizes()
  {
    size_t SizeXYZ = size_t(_size.x()) * _size.y() * _size.z();
    if(SizeXYZ > 0) {
      _texScale = max(_size.x() * _step.x(), max(_size.y() * _step.y(), _size.z() * _step.z()));
  
      if(main() and main()->size() != SizeXYZ)
        main()->allocate();
      if(work() and work()->size() != SizeXYZ)
        work()->allocate();
    } else {
      _texScale = 1.0f;
      if(main())
        main()->reset();
      if(work())
        work()->reset();
    }
  }
  
  void Stack::setSize(const Point3u& size)
  {
    _size = size;
    updateSizes();
  }
  
  void Stack::setStep(const Point3f& step)
  {
    Point3f s = step;
    if(s.x() <= 0)
      s.x() = 1;
    if(s.y() <= 0)
      s.y() = 1;
    if(s.z() <= 0)
      s.z() = 1;
    _step = divide(s, scale());
    //_step = s / scale();
    updateSizes();
  }
  
  void Stack::setOrigin(const Point3f& s)
  {
    _origin = divide(s, scale());
    //_origin = s / scale();
  }
  
  void Stack::center() {
    _origin = -multiply(_step, Point3f(_size)) / 2;
  }
  
  void Stack::resetModified()
  {
    changed_frame = false;
    changed_trans = false;
  }
  
  Matrix4f Stack::worldToImage() const
  {
    Matrix4f m = Matrix4f::identity();
    m(0, 3) = -origin().x() / step().x();
    m(1, 3) = -origin().y() / step().y();
    m(2, 3) = -origin().z() / step().z();
    m(0, 0) = 1. / step().x();
    m(1, 1) = 1. / step().y();
    m(2, 2) = 1. / step().z();
    Matrix4f m2 = Matrix4f::identity();
    m2(0, 3) = m2(1, 3) = m2(2, 3) = -0.5;
    return m;
  }
  
  Matrix4f Stack::imageToWorld() const
  {
    Matrix4f m = Matrix4f::identity();
    m(0, 3) = 0.5 * step().x();
    m(1, 3) = 0.5 * step().y();
    m(2, 3) = 0.5 * step().z();
    m(0, 0) = step().x();
    m(1, 1) = step().y();
    m(2, 2) = step().z();
    Matrix4f m2 = Matrix4f::identity();
    m2(0, 3) = origin().x();
    m2(1, 3) = origin().y();
    m2(2, 3) = origin().z();
    return m2 * m;
  }
  
  Matrix4f Stack::worldToImageVector() const
  {
    Matrix4f m = Matrix4f::identity();
    m(3, 0) = 1. / step().x();
    m(3, 1) = 1. / step().y();
    m(3, 2) = 1. / step().z();
    return m;
  }
  
  Matrix4f Stack::imageToWorldVector() const
  {
    Matrix4f m = Matrix4f::identity();
    m(3, 0) = step().x();
    m(3, 1) = step().y();
    m(3, 2) = step().z();
    return m;
  }
}
