/************
*
*   This file is part of a tool for producing 3D content in the PRC format.
*   Copyright (C) 2008  Orest Shardt <shardtor (at) gmail dot com>
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU Lesser General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*************/

#include "../include/prc/oPRCFile.h"
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <unistd.h>

using namespace std;

void readPoints(istream &is, unsigned int n, double p[][3])
{
  for(unsigned int i = 0; i < n; ++i)
  {
    is >> p[i][0] >> p[i][1] >> p[i][2];
  }
  if(!is)
  {
    cerr << "Error reading list of points." << endl;
    exit(1);
  }
}

void readDoubles(istream &is, unsigned int n, double d[])
{
  for(unsigned int i = 0; i < n; ++i)
  {
    is >> d[i];
  }
  if(!is)
  {
    cerr << "Error reading list of doubles." << endl;
    exit(1);
  }
}

int main(int argc, char **argv)
{
  const char *oFileName = "output.prc";

  int c;
  opterr = 0;
  while ((c = getopt (argc, argv, "o:")) != -1)
    switch (c)
    {
      case 'o':
        oFileName = optarg;
        break;
      case '?':
        if (optopt == 'o')
          cerr << "Option '-o' requires an argument, the filename." << endl;
        else
          cerr << "Unrecognized option '-" << (char)optopt << "'." << endl;
        exit(1);
        break;
      default:
        exit(1);
    }
  istream *ins = NULL;
  if(optind < argc)
  {
    ins = new ifstream(argv[optind]);
    if(!*ins)
    {
      cerr << "Error opening input file " << argv[optind] << endl;
      exit(1);
    }
  }
  else
  {
    ins = &cin;
  }

  ofstream outf(oFileName);
  if(!outf)
  {
    cerr << "Error opening output file " << oFileName << endl;
    exit(1);
  }

  oPRCFile oPRC(outf);

  string entityType;
  while(*ins)
  {
    *ins >> entityType;
    if(!*ins) break;
    if(entityType == "Line")
    {
      double r,g,b,a;
      unsigned int numberOfPoints;
      *ins >> r >> g >> b >> a >> numberOfPoints;
      if(!*ins)
      {
        cerr << "Error reading line data." << endl;
        exit(1);
      }
      else
      {
        double (*points)[3] = new double[numberOfPoints][3];
        readPoints(*ins,numberOfPoints,points);
        oPRC.add(new PRCline(&oPRC,numberOfPoints,points,
                 *new RGBAColour(r,g,b,a)));
      }
    }
    else if(entityType == "Curve")
    {
      double r,g,b,a;
      unsigned int numberOfPoints;
      unsigned int degree;
      *ins >> r >> g >> b >> a >> degree >> numberOfPoints;
      if(!*ins)
      {
        cerr << "Error reading curve data." << endl;
        exit(1);
      }
      else
      {
        double (*points)[3] = new double[numberOfPoints][3];
        double *knots = new double[degree+numberOfPoints+1];
        readPoints(*ins,numberOfPoints,points);
        readDoubles(*ins,degree+numberOfPoints+1,knots);
        oPRC.add(new PRCcurve(&oPRC,degree,numberOfPoints,points,
                 knots,*new RGBAColour(r,g,b,a)));
      }
    }
    else if(entityType == "Surface")
    {
      double r,g,b,a;
      unsigned int numberOfPointsU,numberOfPointsV;
      unsigned int degreeU,degreeV;
      *ins >> r >> g >> b >> a >> degreeU >> degreeV >> numberOfPointsU
          >> numberOfPointsV;
      if(!*ins)
      {
        cerr << "Error reading surface data." << endl;
        exit(1);
      }
      else
      {
        double (*points)[3] = new double[numberOfPointsU*numberOfPointsV][3];
        double *knotsU = new double[degreeU+numberOfPointsU+1];
        double *knotsV = new double[degreeV+numberOfPointsV+1];
        readPoints(*ins,numberOfPointsU*numberOfPointsV,points);
        readDoubles(*ins,degreeU+numberOfPointsU+1,knotsU);
        readDoubles(*ins,degreeV+numberOfPointsV+1,knotsV);
        oPRC.add(new PRCsurface(&oPRC,degreeU,degreeV,numberOfPointsU,
                 numberOfPointsV,points,knotsU,knotsV,
                 *new RGBAColour(r,g,b,a)));
      }
    }
    else
    {
      cerr << "Unrecognized entity type " << entityType << endl;
      exit(1);
    }
  }

  if(ins && ins != &cin)
    delete ins;


  oPRC.finish();

  outf.close();

  return 0;
}
