#include <iostream>
#include <sstream>
#include <string>
using namespace std;

#include <stringconvert.h>
#include <windowscaleD2.h>
#include <zero.h>

windowscaleD2 windowscaleD2::unit(0,0,1,1);
windowscaleD2 windowscaleD2::unitcentered(-0.5,-0.5,0.5,0.5);

windowscaleD2::windowscaleD2()
{
}

void windowscaleD2::construct
(
  doublec xmin_, 
  doublec ymin_, 
  doublec xmax_, 
  doublec ymax_ 
)
{
  xmin=xmin_;
  ymin=ymin_;
  xmax=xmax_;
  ymax=ymax_;

  update();
}

windowscaleD2::windowscaleD2
(
  doublec xmin_, 
  doublec ymin_, 
  doublec xmax_, 
  doublec ymax_ 
)
  : xmin(xmin_), ymin(ymin_), xmax(xmax_), ymax(ymax_)
{
  update();
}



boolc windowscaleD2::unitscale(double & x, double & y) const
{
  bool res=true;

  assert(oneoverxlength!=0);
  assert(oneoverylength!=0);
  x = (x-xmin)*oneoverxlength;
  y -= ymin;
  y *= oneoverylength;
  //y = (y-ymin)*oneoverylength;

/*
  if (unitscale_inside)
  {
    assert(x>=0.0);
    assert(y>=0.0);
    assert(x<=1.0);
    assert(y<=1.0);
  }
*/

//TODO
  res &= (x+zero<double>::val>=0.0);
  res &= (y+zero<double>::val>=0.0);
  res &= (x<=(double)1.0+zero<double>::val);
  res &= (y<=(double)1.0+zero<double>::val);

#ifndef NDEBUG
//if (isinside(x,y)==false)
//{
//  cout << "*this=" << (stringc)(*this) << endl;
//  cout << SHOW(x) << " " SHOW(y) << endl;
//  cout << SHOW(ymax-y) << endl;
//}
#endif

  //assert( isinside(x,y) );
  return res;
}

void windowscaleD2::unitscaleInverse(double & x, double & y) const
{
  x = (xmax-xmin)*x + xmin;
  y = (ymax-ymin)*y + ymin;
}

void windowscaleD2::unitscaleInverse_x(double & x) const
{
  x = (xmax-xmin)*x + xmin;
}

void windowscaleD2::unitscaleInverse_y(double & y) const
{
  y = (ymax-ymin)*y + ymin;
}

// Includes boundary.
boolc windowscaleD2::isinside(doublec x, doublec y) const
{
//cout << "isinside" << SHOW(x) << " " << SHOW(y) << endl;
  if (x+zero<double>::val<xmin)
    return false;

  if (x>xmax+zero<double>::val)
    return false;

  if (y+zero<double>::val<ymin)
    return false;

  if (y>ymax+zero<double>::val)
    return false;

  return true;
}

doublec windowscaleD2::widthoverheight() const
{
  assert(ymax-ymin!=0);
  return (xmax-xmin)/(ymax-ymin);
}

doublec windowscaleD2::heightoverwidth() const
{
  assert(xmax-xmin!=0);
  return (ymax-ymin)/(xmax-xmin);
}


void windowscaleD2::update()
{
  oneoverxlength = (double)1.0;
  oneoverxlength /= (double)(xmax-xmin);
  oneoverylength = (double)1.0;
  oneoverylength /= (double)(ymax-ymin);

  assert(oneoverxlength!=0);
  assert(oneoverylength!=0);
}

void windowscaleD2::shiftx(doublec x)
{
  xmin += x;
  xmax += x;
}

void windowscaleD2::shifty(doublec x)
{
  ymin += x;
  ymax += x;
}

void windowscaleD2::shiftxy(doublec x, doublec y)
{
  shiftx(x);
  shifty(y);
}

void windowscaleD2::shiftquadrant1()
{
  shiftxy(-xmin,-ymin);
}

void windowscaleD2::shiftquadrant2()
{
  shiftxy(-xmax,-ymin);
}

void windowscaleD2::shiftquadrant3()
{
  shiftxy(-xmax,-ymax);
}

void windowscaleD2::shiftquadrant4()
{
  shiftxy(-xmin,-ymax);
}

void windowscaleD2::shiftcenterx()
{
  double x = (xmax+xmin)*(double)0.5;
  shiftx(x);
}

void windowscaleD2::shiftcentery()
{
  double y = (ymax+ymin)*(double)0.5;
  shifty(y);
}

void windowscaleD2::shiftcenterxy()
{
  shiftcenterx();
  shiftcentery();
}

void windowscaleD2::scalex(doublec scale)
{
  xmin *= scale;
  xmax *= scale;
}

void windowscaleD2::scaley(doublec scale)
{
  ymin *= scale;
  ymax *= scale;
}

void windowscaleD2::scalexy(doublec scale)
{
  scalex(scale);
  scaley(scale);
}

void windowscaleD2::unitwindow()
{
  xmin=0;
  xmax=1;
  ymin=0;
  ymax=1;

  update();
}


windowscaleD2::operator stringc () const
{
  string s;
  string s2;

  stringto(s2,xmin);
  s += s2;
  s += " ";

  stringto(s2,ymin);
  s += s2;
  s += " ";

  stringto(s2,xmax);
  s += s2;
  s += " ";

  stringto(s2,ymax);
  s += s2;

  return s;
}

void windowscaleD2::serializeInverse(stringc & s)
{
  stringstream ss(s);
  ss >> xmin;
  ss >> ymin;
  ss >> xmax;
  ss >> ymax;

//cout << SHOW(xmin) << endl;
//cout << SHOW(ymin) << endl;
//cout << SHOW(xmax) << endl;
//cout << SHOW(ymax) << endl;

  update();
}

boolc windowscaleD2::convertfrom
(
  double & x, 
  double & y, 
  windowscaleD2 const & w2
) const
{
  bool res=true;

  //double x2(x);
  //double y2(y);
  res &= w2.unitscale(x,y);
  unitscaleInverse(x,y);

  return res;
}


