//
// 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 "ImageData.hpp"

#include "ClipRegion.hpp"
#include "Information.hpp"
#include "Vector.hpp"

namespace mgx {
  typedef Vector<16, double> Point16d;
  
  ClipRegion::ClipRegion() {}
  
  void ClipRegion::setClip(Clip* c)
  {
    clip = c;
    int clipNo = c->clipNo();
  
    if(clipNo == 0) {
      clip0 = GL_CLIP_PLANE0;
      clip1 = GL_CLIP_PLANE1;
      GridColor = Colors::Clip1GridColor;
    } else if(clipNo == 1) {
      clip0 = GL_CLIP_PLANE2;
      clip1 = GL_CLIP_PLANE3;
      GridColor = Colors::Clip2GridColor;
    } else if(clipNo == 2) {
      clip0 = GL_CLIP_PLANE4;
      clip1 = GL_CLIP_PLANE5;
      GridColor = Colors::Clip3GridColor;
    } else
      Information::out << "ClipRegion::ClipRegion:Error invalid clipNo-" << clipNo << endl;
  }
  
  // Read clipping plane parameters
  void ClipRegion::readParms(Parms& parms, QString section)
  {
    // Taken from Model readparm
    bool Enable, Grid;
    float Width;
    parms(section, "Enable", Enable, false);
    parms(section, "Grid", Grid, false);
    parms(section, "Width", Width, .5f);
    if(Enable)
      clip->enable();
    else
      clip->disable();
    if(Grid)
      clip->showGrid();
    else
      clip->hideGrid();
    clip->setWidth(Width);
  
    float GridSize;
    uint GridSquares;
    // View file only parameters
    parms(section, "GridSize", GridSize, 1.0f);
    clip->setGridSize(GridSize);
    parms(section, "GridSquares", GridSquares, 3u);
    clip->setGridSquares(GridSquares);
    // parms(section, "GridColor", GridColor, 48u);
    Point3f nrml(0, 0, 0);
    nrml[clip->clipNo()] = 1.0;
    Point3f Normal;
    parms(section, "Normal", Normal, nrml);
    if(norm(Normal) == 0.0)
      Normal[clip->clipNo()] = 1.0;
    clip->setNormal(Normal);
    Matrix4d m = 1;
    parms(section, "Frame", m, m);
    clip->frame().setFromMatrix(m.c_data());
    clip->hasChanged();
  }
  
  // Read clipping plane parameters
  void ClipRegion::writeParms(QTextStream& pout, QString section)
  {
    // Taken from Model readparm
    pout << endl;
    pout << "[" << section << "]" << endl;
    pout << "Enable: " << (clip->enabled() ? "true" : "false") << endl;
    pout << "Grid: " << (clip->grid() ? "true" : "false") << endl;
    pout << "Width: " << clip->width() << endl;
  
    // View file only parameters
    pout << "GridSize: " << clip->gridSize() << endl;
    pout << "GridSquares: " << clip->gridSquares() << endl;
    pout << "GridColor: " << GridColor << endl;
    pout << "Normal: " << clip->normal() << endl;
    pout << "Frame: " << Point16d(clip->frame().matrix()) << endl;
    pout << endl;
  }
  
  // Draw (use) clipping place (ax + by + cz + d = 0)
  void ClipRegion::drawClip()
  {
    if(!clip->enabled()) {
      disable();
      return;
    }
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMultMatrixd(clip->frame().matrix());
  
    /*
       const Point3f& Normal = clip->normal();
       float Width = clip->width();
  
       Point4d cp0(-Normal.x(), -Normal.y(), -Normal.z(), Width);
       Point4d cp1(Normal.x(), Normal.y(), Normal.z(), Width);
       glClipPlane(clip0, cp0.c_data());
       glClipPlane(clip1, cp1.c_data());
     */
    Point4d cp0(clip->normalFormNeg());
    Point4d cp1(clip->normalFormPos());
    glClipPlane(clip0, cp0.c_data());
    glClipPlane(clip1, cp1.c_data());
    glEnable(clip0);
    glEnable(clip1);
    glPopMatrix();
  }
  
  // Draw (use) clipping place (ax + by + cz + d = 0)
  void ClipRegion::drawGrid(float size)
  {
    if(!clip->grid())
      return;
  
    glDisable(GL_LIGHTING);
    glDisable(GL_BLEND);
    // glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_1D);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_TEXTURE_3D);
  
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glMultMatrixd(clip->frame().matrix());
  
    Point3f n = clip->normal();
    n.normalize();
    float s = size * .5;
    float d = s * 2.0 / float(clip->gridSquares());
    n *= clip->width() - .001;
  
    Color3f gridColor = Colors::getColor(GridColor);
    glColor3fv(gridColor.data());
    glLineWidth(1);
    Point3f xb = clip->xb();
    Point3f yb = clip->yb();
    glBegin(GL_LINES);
    for(uint i = 0; i <= clip->gridSquares(); i++) {
      float t = -s + d * i;
      glVertex3fv((n + xb * s + yb * t).c_data());
      glVertex3fv((n - xb * s + yb * t).c_data());
      glVertex3fv((n + yb * s + xb * t).c_data());
      glVertex3fv((n - yb * s + xb * t).c_data());
  
      glVertex3fv((-n + xb * s + yb * t).c_data());
      glVertex3fv((-n - xb * s + yb * t).c_data());
      glVertex3fv((-n + yb * s + xb * t).c_data());
      glVertex3fv((-n - yb * s + xb * t).c_data());
    }
    for(uint i = 0; i <= clip->gridSquares(); i++) {
      for(uint j = 0; j <= clip->gridSquares(); j++) {
        glVertex3fv((n + xb * (-s + d * i) + yb * (-s + d * j)).c_data());
        glVertex3fv((-n + xb * (-s + d * i) + yb * (-s + d * j)).c_data());
      }
    }
    glEnd();
    glPopMatrix();
  }
}
