/*******************************************************************************
*                                                                              *
* Author        :  Angus Johnson                                               *
* Version       :  1.1                                                         *
* Date          :  4 April 2011                                                *
* Copyright :  Angus Johnson 2010-2011                                         *
*                                                                              *
* License:                                                                     *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt                                         *
*                                                                              *
* Modified by Mike Owens to support coordinate transformation                  *
*******************************************************************************/

#include <stdexcept>
#include <cmath>
#include <iostream>
#include <cairo.h>
#include "clipper.hpp"
#include "cairo_clipper.hpp"

namespace ClipperLib {
    namespace cairo {

        namespace {

            inline cInt Round(double val)
            {
                if ((val < 0)) return (cInt)(val - 0.5); else return (cInt)(val + 0.5);
            }

            void transform_point(cairo_t* pen, Transform transform, cInt* x, cInt* y)
            {
                double _x = (double)*x, _y = (double)*y;
                switch (transform)
                {
                    case tDeviceToUser:
                        cairo_device_to_user(pen, &_x, &_y);
                        break;
                    case tUserToDevice:
                        cairo_user_to_device(pen, &_x, &_y);
                        break;
                    default:
                        ;
                }
                *x = Round(_x); *y = Round(_y);
            }
        }

        void cairo_to_clipper(cairo_t* cr,
                                                    Paths &pg,
                                                    int scaling_factor,
                                                    Transform transform)
        {
            if (scaling_factor > 8 || scaling_factor < 0)
                throw clipperCairoException("cairo_to_clipper: invalid scaling factor");
            double scaling = std::pow((double)10, scaling_factor);

            pg.clear();
            cairo_path_t *path = cairo_copy_path_flat(cr);

            int poly_count = 0;
            for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
                if( path->data[i].header.type == CAIRO_PATH_CLOSE_PATH) poly_count++;
            }

            pg.resize(poly_count);
            int i = 0, pc = 0;
            while (pc < poly_count)
            {
                int vert_count = 1;
                int j = i;
                while(j < path->num_data && path->data[j].header.type != CAIRO_PATH_CLOSE_PATH)
                {
                    if (path->data[j].header.type == CAIRO_PATH_LINE_TO)
                        vert_count++;
                    j += path->data[j].header.length;
                }
                pg[pc].resize(vert_count);
                if (path->data[i].header.type != CAIRO_PATH_MOVE_TO) {
                    pg.resize(pc);
                    break;
                }
                pg[pc][0].X = Round(path->data[i+1].point.x *scaling);
                pg[pc][0].Y = Round(path->data[i+1].point.y *scaling);
                if (transform != tNone)
                    transform_point(cr, transform, &pg[pc][0].X, &pg[pc][0].Y);

                i += path->data[i].header.length;

                j = 1;
                while (j < vert_count && i < path->num_data && path->data[i].header.type == CAIRO_PATH_LINE_TO) {
                    pg[pc][j].X = Round(path->data[i+1].point.x *scaling);
                    pg[pc][j].Y = Round(path->data[i+1].point.y *scaling);
                    if (transform != tNone)
                        transform_point(cr, transform, &pg[pc][j].X, &pg[pc][j].Y);
                    j++;
                    i += path->data[i].header.length;
                }
                pc++;
                i += path->data[i].header.length;
            }
            cairo_path_destroy(path);
        }
        //--------------------------------------------------------------------------

        void clipper_to_cairo(const Paths &pg, cairo_t* cr, int scaling_factor, Transform transform)
        {
            if (scaling_factor > 8 || scaling_factor < 0)
                throw clipperCairoException("clipper_to_cairo: invalid scaling factor");
            double scaling = std::pow((double)10, scaling_factor);
            for (size_t i = 0; i < pg.size(); ++i)
            {
                size_t sz = pg[i].size();
                if (sz < 3)
                    continue;
                cairo_new_sub_path(cr);
                //std::cerr << "sub path";
                for (size_t j = 0; j < sz; ++j) {
                    cInt x = pg[i][j].X, y = pg[i][j].Y;
                    if (transform != tNone)
                        transform_point(cr, transform, &x, &y);
                    //std::cerr << " (" << (double)x / scaling << "," << (double)y / scaling << ")";
                    cairo_line_to(cr, (double)x / scaling, (double)y / scaling);
                }
                //std::cerr << std::endl;
                cairo_close_path(cr);
            }
        }
        //--------------------------------------------------------------------------

    }
}