/**
 * Copyright (C) 2016  Alexander Schmidt
 *
 * This file is part of Seamulator.
 *
 * Seamulator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Seamulator 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Seamulator.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctime>
#include <cmath>
#include <iostream>
#include <memory>

#include <boost/program_options.hpp>
#include <GL/glut.h>

#include "sea.h"
#include "seaview.h"
#include "watersurface.h"

const int INIT_WINDOW_POS_X{50};
const int INIT_WINDOW_POS_Y{50};
const int INIT_WINDOW_WIDTH{800};
const int INIT_WINDOW_HEIGHT{600};
const double INIT_VIEW_AZIMUTH{0};
const double INIT_VIEW_ALTITUDE{M_PI / 4};
const char WINDOW_TITLE[]{"seamulator"};

SeaPtr sea;
WaterSurfacePtr surface;
std::unique_ptr<SeaView> seaView;

struct Settings {
  double windSpeed;
  int latticeSize;
  double latticeExtend;
  double amplitudeFactor;
};

void glDisplayFunc();
void glReshapeFunc(int width, int height);
void glMouseFunc(int button, int state, int x, int y);
void glMotionFunc(int x, int y);
Settings parseArguments(int argc, char** argv);

namespace po = boost::program_options;

int main(int argc, char** argv)
{
  Settings settings = parseArguments(argc, argv);

  std::srand(std::time(0));
  surface = std::make_shared<WaterSurface>(settings.latticeSize,
					   settings.latticeExtend);
  sea = std::make_shared<Sea>(surface,
			      settings.windSpeed,
			      settings.amplitudeFactor);
  seaView = std::make_unique<SeaView>(settings.latticeExtend * 1.5,
				      INIT_VIEW_AZIMUTH,
				      INIT_VIEW_ALTITUDE);

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE);
  glutInitWindowSize(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT);
  glutInitWindowPosition(INIT_WINDOW_POS_X, INIT_WINDOW_POS_Y);
  glutCreateWindow(WINDOW_TITLE);
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

  glutDisplayFunc(glDisplayFunc);
  glutReshapeFunc(glReshapeFunc);
  glutMouseFunc(glMouseFunc);
  glutMotionFunc(glMotionFunc);

  glutMainLoop();

  return 0;
}

Settings parseArguments(int argc, char** argv)
{
  po::options_description desc("Available options");
  desc.add_options()
    ("help,h", "show this help")
    ("windspeed,w", po::value<double>()->default_value(10),
     "wind speed (m/s)")
    ("size,s", po::value<char>()->default_value('s'),
     "lattice size (s|l)")
    ("amplitude,a", po::value<double>()->default_value(1),
     "amplitude multiplicator")
    ;

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  if (vm.count("help")) {
    std::cout << desc << "\n";
    exit(1);
  }

  Settings settings;

  settings.windSpeed = vm["windspeed"].as<double>();

  if (vm["size"].as<char>() == 'l') {
    settings.latticeSize = 256;
    settings.latticeExtend = 20;
    settings.amplitudeFactor = vm["amplitude"].as<double>() * 0.00000004;
  }
  else {
    settings.latticeSize = 128;
    settings.latticeExtend = 10;
    settings.amplitudeFactor = vm["amplitude"].as<double>() * 0.0000001;
  }

  return settings;
}

void glDisplayFunc()
{
  glClear(GL_COLOR_BUFFER_BIT);

  seaView->setupView();
  sea->update();
  surface->draw();

  glutSwapBuffers();
  glutPostRedisplay();
}

void glReshapeFunc(int width, int height)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(50.0, ((float)width/(float)height), 0, 1000.0);
  glViewport(0, 0, width, height);
}

void glMouseFunc(int button, int state, int x, int y)
{
  seaView->onMouseEvent(button, state, x, y);
}

void glMotionFunc(int x, int y)
{
  seaView->onMouseMove(x, y);
}
