/*
 * Copyright © 2012 Keith Packard <keithp@keithp.com>
 *
 * This program 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; version 2 of the License.
 *
 * 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

autoload Cairo;
autoload Client;
autoload Client::SVG;

extend namespace Client {

	public namespace Draw {

		import Cairo;
		import Cairo::Rsvg;
		import RR;

		public typedef struct {
			int	xoff, yoff;
			real	xscale, yscale;
		} transform_t;

		typedef struct {
			Cairo::surface_t	surface;
			int			width, height;
			rsvg_t			svg;
		} sprite_t;

		sprite_t sprite_from_string(string xml) = (sprite_t) {
			.svg = Cairo::Rsvg::new_from_string(xml),
			.width = -1,
			.height = -1
		};

		sprite_t[2]	cell = { sprite_from_string(Client::SVG::cell1),
					 sprite_from_string(Client::SVG::cell2) };

		sprite_t[Color]	robots = {
			Color.Red => sprite_from_string(Client::SVG::robot_red),
			Color.Blue => sprite_from_string(Client::SVG::robot_blue),
			Color.Yellow => sprite_from_string(Client::SVG::robot_yellow),
			Color.Green => sprite_from_string(Client::SVG::robot_green) };

		sprite_t robot_shadow = sprite_from_string(Client::SVG::robot_shadow);

		sprite_t[Color][Shape]	targets = {
			Color.Red => {
				Shape.Triangle => sprite_from_string(Client::SVG::target_red_triangle),
				Shape.Square => sprite_from_string(Client::SVG::target_red_square),
				Shape.Octagon => sprite_from_string(Client::SVG::target_red_octagon),
				Shape.Circle => sprite_from_string(Client::SVG::target_red_circle),
			},
			Color.Yellow => {
				Shape.Triangle => sprite_from_string(Client::SVG::target_yellow_triangle),
				Shape.Square => sprite_from_string(Client::SVG::target_yellow_square),
				Shape.Octagon => sprite_from_string(Client::SVG::target_yellow_octagon),
				Shape.Circle => sprite_from_string(Client::SVG::target_yellow_circle),
			},
			Color.Green => {
				Shape.Triangle => sprite_from_string(Client::SVG::target_green_triangle),
				Shape.Square => sprite_from_string(Client::SVG::target_green_square),
				Shape.Octagon => sprite_from_string(Client::SVG::target_green_octagon),
				Shape.Circle => sprite_from_string(Client::SVG::target_green_circle),
			},
			Color.Blue => {
				Shape.Triangle => sprite_from_string(Client::SVG::target_blue_triangle),
				Shape.Square => sprite_from_string(Client::SVG::target_blue_square),
				Shape.Octagon => sprite_from_string(Client::SVG::target_blue_octagon),
				Shape.Circle => sprite_from_string(Client::SVG::target_blue_circle),
			},
			Color.Whirl => {
				Shape.Whirl => sprite_from_string(Client::SVG::target_whirl),
			},
		};

		sprite_t 		wall = sprite_from_string(Client::SVG::wall);

		void draw_sprite(&sprite_t sprite, cairo_t cr) {
			render(sprite.svg, cr);
		}

		dimensions_t cell_dim  = get_dimensions(cell[0].svg);
		dimensions_t wall_dim  = get_dimensions(wall.svg);

		public int cell_width = cell_dim.width;
		public int cell_height = cell_dim.height;

		public int wall_thickness = wall_dim.height;

		void draw_cached_sprite(&sprite_t sprite, cairo_t cr, &transform_t t, real alpha) {
			int	width = ceil(cell_width * t.xscale);
			int	height = ceil(cell_width * t.yscale);

			save(cr);
			if (width != sprite.width || height != sprite.height) {
				if (!is_uninit(&sprite.surface))
					Cairo::Surface::destroy(sprite.surface);
				sprite.width = width;
				sprite.height = height;
				if (width > 0 && height > 0) {
					sprite.surface = Cairo::Surface::create_similar(Cairo::get_target(cr),
											Cairo::content_t.COLOR_ALPHA,
											width, height);
					cairo_t scr = Cairo::create(sprite.surface);
					scale(scr, t.xscale, t.yscale);
					draw_sprite(&sprite, scr);
					Cairo::destroy(scr);
				}
			}
			if (sprite.width > 0 && sprite.height > 0) {
				set_source_surface(cr, sprite.surface, 0, 0);
				if (alpha != 1)
					paint_with_alpha(cr, alpha);
				else
					paint(cr);
			}
			restore(cr);
		}

		public void background (cairo_t cr, int x, int y, RR::Object object, &transform_t t) {
			save(cr);
			translate(cr, x * cell_width * t.xscale + t.xoff, y * cell_height * t.yscale + t.yoff);

			draw_cached_sprite(&cell[x+y & 1], cr, &t, 1);
			restore(cr);
		}
		
		public void walls(cairo_t cr, int x, int y, RR::Object object, &transform_t t) {
			save(cr);
			translate(cr, t.xoff, t.yoff);
			scale(cr, t.xscale, t.yscale);
			translate(cr, x * cell_width, y * cell_height);
			/*
			rectangle(cr, 0, 0, cell_width, cell_height);
			clip(cr);
			*/

			void draw_wall (bool doit, bool vertical, bool shift) {
				if (!doit) return;
				save(cr);
				if (vertical) {
					rotate(cr, pi/2);
					if (shift)
						translate(cr, 0, -cell_height);
				} else if (shift)
						translate(cr, 0, cell_height);
				draw_sprite(&wall, cr);
				restore(cr);
			}
			draw_wall (object.walls.left, true, false);
			draw_wall (object.walls.above, false, false);
			if (x == 15)
				draw_wall (object.walls.right, true, true);
			if (y == 15)
				draw_wall (object.walls.below, false, true);
			restore(cr);
		}
		
		public void contents (cairo_t cr, int x, int y, RR::Object object,
				      RR::TargetOrNone active_target,
				      RR::RobotOrNone active_robot,
				      &transform_t t) {
			save(cr);
			translate(cr, x * cell_width * t.xscale + t.xoff, y * cell_height * t.yscale + t.yoff);

			union switch (object.target) {
			case none:
				break;
			case target target:
				real alpha = 1;
				if (object.target != active_target)
					alpha = 0.25;
				draw_cached_sprite(&targets[target.color][target.shape], cr, &t, alpha);
				break;
			}
			union switch(object.robot) {
			case none:
				break;
			case robot r:
				union switch (active_robot) {
				case robot a:
					if (a.color == r.color) {
						save(cr);
						scale(cr, t.xscale, t.yscale);
						translate(cr, 5, 1);
						scale(cr, 1.1, 1.1);
						draw_sprite(&robot_shadow, cr);
						restore(cr);
					}
					break;
				default:
				}
				draw_cached_sprite(&robots[r.color], cr, &t, 1);
				break;
			}
			restore(cr);
		}

		public void target (cairo_t cr, real x, real y, TargetOrNone target, &transform_t t) {
			save(cr);
			translate(cr, x * cell_width * t.xscale + t.xoff, y * cell_height * t.yscale + t.yoff);
			scale(cr, 2 * t.xscale, 2 *t.yscale);
			union switch (target) {
			case target t:
				draw_sprite(&targets[t.color][t.shape], cr);
				break;
			default:
				break;
			}
			restore(cr);
		}
	}
}
