(*
 *  This file is part of SwfLib
 *  Copyright (c)2005 Nicolas Cannasse
 *
 *  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; either version 2 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 *)

open Png
open Swf
open ExtList

type error_msg =
	| PngError of Png.error_msg
	| Interlaced
	| UnsupportedColorModel
	| UnsupportedExtension
	| UnzipFailed

exception Error of error_msg
exception File_not_found of string

type picture = {
	pwidth : int;
	pheight : int;
	pid : int;
	pdata : tag_data;
	pframe : string option;
}

let error_msg = function
	| PngError m -> Png.error_msg m
	| Interlaced -> "Interlaced mode is not supported"
	| UnsupportedColorModel -> "Unsupported color model"
	| UnsupportedExtension -> "Unsupported file extension"
	| UnzipFailed -> "Decompression failed"

let error msg = raise (Error msg)

let unsigned v n =
	if v < 0 then
		(- ( v + 1 )) lxor (1 lsl n - 1)
	else
		v

let load_picture file id =
	let ch = IO.input_channel (try open_in_bin file with _ -> raise (File_not_found file)) in
	let len = String.length file in
	let p = (try String.rindex file '.' with Not_found -> len) in
	let ext = String.sub file (p + 1) (len - (p + 1)) in
	match String.uppercase ext with
	| "PNG" ->
		let png , header, data = (try
			let p = Png.parse ch in
			p , Png.header p, Png.data p
		with Png.Error msg ->
			IO.close_in ch; error (PngError msg)
		) in
		IO.close_in ch;
		if header.png_interlace then error Interlaced;
		let data = (try Extc.unzip data with _ -> error UnzipFailed) in
		let w = header.png_width in
		let h = header.png_height in
		let data = (try Png.filter png data with Png.Error msg -> error (PngError msg)) in
		let data = Bytes.unsafe_of_string data in
		{
			pwidth = w;
			pheight = h;
			pid = id;
			pframe = None;
			pdata = (match header.png_color with
				| ClTrueColor (TBits8,NoAlpha) ->
					(* set alpha to 0 *)
					for p = 0 to w * h - 1 do
						Bytes.unsafe_set data (p * 4) '\000';
					done;
					TBitsLossless {
						bll_id = id;
						bll_format = 5;
						bll_width = w;
						bll_height = h;
						bll_data = Extc.zip (Bytes.unsafe_to_string data);
					}
				| ClTrueColor (TBits8,HaveAlpha) ->
					(* premultiply rgb by alpha *)
					for p = 0 to w * h - 1 do
						let k = p * 4 in
						let a = int_of_char (Bytes.unsafe_get data k) in
						Bytes.unsafe_set data (k + 1) (Char.unsafe_chr ((int_of_char (Bytes.unsafe_get data (k + 1)) * a) / 0xFF));
						Bytes.unsafe_set data (k + 2) (Char.unsafe_chr ((int_of_char (Bytes.unsafe_get data (k + 2)) * a) / 0xFF));
						Bytes.unsafe_set data (k + 3) (Char.unsafe_chr ((int_of_char (Bytes.unsafe_get data (k + 3)) * a) / 0xFF));
					done;
					TBitsLossless2 {
						bll_id = id;
						bll_format = 5;
						bll_width = w;
						bll_height = h;
						bll_data = Extc.zip (Bytes.unsafe_to_string data);
					}
				| _ -> error UnsupportedColorModel);
		}
	| _ ->
		IO.close_in ch;
		error UnsupportedExtension

let make_clip name pics baseid =
	let npics = List.length pics in
	let ids = Array.of_list (List.map (fun p -> p.pid) pics) in
	let rec loop i p =
		let w = p.pwidth in
		let h = p.pheight in
		let rb = if 20 * max w h >= 1 lsl 14 then 15 else 14 in
		let nbits = rb in
		TShape {
			sh_id = baseid + i;
			sh_bounds = {
				rect_nbits = rb;
				left = 0;
				top = 0;
				right = w * 20;
				bottom = h * 20;
			};
			sh_bounds2 = None;
			sh_style = {
				sws_fill_styles = [
					SFSBitmap {
						sfb_repeat = true;
						sfb_smooth = true;
						sfb_cid = ids.(i);
						sfb_mpos = {
							scale = Some {
								m_nbits = 22;
								mx = 20 lsl 16;
								my = 20 lsl 16;
							};
							rotate = None;
							trans = {
								m_nbits = 0;
								mx = 0;
								my = 0;
							};
						};
					};
				];
				sws_line_styles = [];
				sws_records = {
					srs_nlbits = 0;
					srs_nfbits = 1;
					srs_records = [
						SRStyleChange {
							scsr_move = None;
							scsr_fs0 = None;
							scsr_fs1 = Some 1;
							scsr_ls = None;
							scsr_new_styles = None;
						};
						SRStraightEdge {
							sser_nbits = nbits;
							sser_line = Some (w * 20) , None;
						};
						SRStraightEdge {
							sser_nbits = nbits;
							sser_line = None , Some (h * 20);
						};
						SRStraightEdge {
							sser_nbits = nbits;
							sser_line = Some (unsigned (-w * 20) nbits), None;
						};
						SRStraightEdge {
							sser_nbits = nbits;
							sser_line = None , Some (unsigned (-h * 20) nbits);
						};
					];
				};
			};
		}
	in
	let shapes = List.mapi loop pics in
	let rec loop i =
		if i = npics then
			[]
		else
			TPlaceObject2 {
				po_depth = 0;
				po_move = (i > 0);
				po_cid = Some (baseid+i);
				po_color = None;
				po_matrix = None;
				po_ratio = None;
				po_inst_name = None;
				po_clip_depth = None;
				po_events = None;
				po_filters = None;
				po_blend = None;
				po_bcache = None;
			} :: TShowFrame :: loop (i+1)
	in
	let tid = ref 0 in
	let make_tag t =
		incr tid;
		{
			tid = - !tid;
			textended = false;
			tdata = t;
		}
	in
	let pics = List.map (fun p -> make_tag p.pdata) pics in
	let shapes = List.map make_tag shapes in
	pics @ shapes @ List.map make_tag [
		TClip {
			c_id = baseid + npics;
			c_frame_count = npics;
			c_tags = List.map make_tag (loop 0);
		};
		TExport [{
			exp_id = baseid + npics;
			exp_name = name;
		}];
	]
