/* jpg2swf.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2006-2009 */
/* on an idea/request by Krishnan ? */

/*

Copyright (c) 2006-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/



#include	<sswf/libsswf.h>


char *progname;
const char *version = "1.0";

char *output;
int quality = 90;
int errcnt;
int remove_image = -1;
int loop;
int blankout;
long background_color = 0x00FFFFFF;	// white
sswf::TagHeader *my_header;
float rate = 1.0;	// by default, no redraw is necessary, use a very slow rate


void remove_image_now(void)
{
	// Remove the image once we're done with it
	sswf::TagRemove *remove = new sswf::TagRemove(my_header);
	remove->SetDepth(1);
	remove->SetObjectID(remove_image);
	remove_image = -1;
}

void save_movie(const char *image_filename)
{
	// Check whether we should remove the last image
	if((loop != 0 || blankout != 0) && remove_image != -1) {
		remove_image_now();
	}

	// If not looping, stop the movie with a script
	if(loop == 0) {
		sswf::TagDoAction *do_action = new sswf::TagDoAction(my_header);
		sswf::Action stop(my_header, sswf::Action::ACTION_STOP);
		do_action->SetAction(stop);
		// the following is automatic, could be removed
		sswf::Action end(my_header, sswf::Action::ACTION_END);
		do_action->SetAction(end);

		// Execute the stop and wait here
		(void) new sswf::TagShowFrame(my_header);
	}

	// End the Flash movie
	(void) new sswf::TagEnd(my_header);

	// Save the result in a file
	sswf::Data result;
	size_t size;
	void *buffer;
	if(my_header->Save(result) != 0) {
		fprintf(stderr, "ERROR: something went wrong while trying to save to swf format.\n");
	}
	else {
		result.Read(buffer, size);

		char *out_filename;
		if(output == 0) {
			int len = strlen(image_filename) + 1 + 4;
			out_filename = new char[len];
			const char *e, *s = image_filename;
			char *o = out_filename;
			e = 0;
			while(*s != '\0') {
				if(*s == '.') {
					e = s;
				}
				*o++ = *s++;
			}
			if(e != 0) {
				o += e - s;
			}
			o[0] = '.';
			o[1] = 's';
			o[2] = 'w';
			o[3] = 'f';
			o[4] = '\0';
		}
		else {
			out_filename = output;
		}

		FILE *f = fopen(out_filename, "wb");
		if(f == 0) {
			fprintf(stderr, "ERROR: can't open \"%s\" for writing.\n", out_filename);
		}
		else {
			fwrite(buffer, size, 1, f);
			fclose(f);
		}

		if(output == 0) {
			delete out_filename;
		}
	}

	// we need another header if we have other image filenames
	// on the command line
	delete my_header;
	my_header = 0;

	return;
}


void create_movie(const char *image_filename)
{
	if(my_header != 0) {
		save_movie(image_filename);
	}

	// Initializes the header
	my_header = new sswf::TagHeader;
	my_header->SetRate(rate);

	// Setup the background color
	sswf::TagSetBackgroundColor *bck_color = new sswf::TagSetBackgroundColor(my_header);
	sswf::Color color;
	color.Set((background_color >> 16) & 255,
			(background_color >> 8) & 255,
				background_color & 255);
	bck_color->SetColor(color);

	return;
}



void convert_image(const char *image_filename)
{
	bool need_close = false;
	if(my_header == 0) {
		need_close = true;
		create_movie(image_filename);
	}

	if(remove_image != -1) {
		remove_image_now();
	}

	// Loading an image
	sswf::TagImage *image = new sswf::TagImage(my_header);
	image->SetQuality(quality);
	image->SetFormat(sswf::TagImage::IMAGE_FORMAT_JPEG);
	if(image->SetFilename(image_filename, 0) != 0) {
		fprintf(stderr, "ERROR: can't load \"%s\".\n", image_filename);
		errcnt++;
		return;
	}
	sswf::TagImage::image_t bitmap;
	image->GetImageData(bitmap);

	// Now we can define the size of the frame rectangle in the header
	sswf::SRectangle rect;
	rect.Set(0, bitmap.f_width * 20, 0, bitmap.f_height * 20);
	my_header->SetFrame(rect);

	// Put the image in a shape
	sswf::Style style(*my_header);
	style.SetType(sswf::Style::STYLE_TYPE_BITMAP_CLIPPED);
	style.SetBitmap(image->Identification());
	sswf::Matrix matrix;
	matrix.SetScale(20, 20);
	matrix.SetTranslate(bitmap.f_width * -10, bitmap.f_height * -10);
	style.SetMatrix(0, matrix);

	sswf::TagShape *shape = new sswf::TagShape(my_header);
	rect.Set(bitmap.f_width  * -10 - 20, bitmap.f_width  * 10 + 20,
		 bitmap.f_height * -10 - 20, bitmap.f_height * 10 + 20);
	shape->SetBounds(0, rect);
	shape->AddStyle(style);
	shape->AddMove(sswf::TagShape::MORPH_MODE_SHAPE0, bitmap.f_width * -10 - 20, bitmap.f_height * -10 - 20);
	shape->AddEdge(sswf::TagShape::MORPH_MODE_SHAPE0, bitmap.f_width * 20 + 40, 0);
	shape->AddEdge(sswf::TagShape::MORPH_MODE_SHAPE0, 0, bitmap.f_height * 20 + 40);
	shape->AddEdge(sswf::TagShape::MORPH_MODE_SHAPE0, -bitmap.f_width * 20 - 40, 0);
	shape->AddEdge(sswf::TagShape::MORPH_MODE_SHAPE0, 0, -bitmap.f_height * 20 - 40);

	// The usual is to put everything in a sprite... (that's not required)
	sswf::TagSprite *sprite = new sswf::TagSprite(my_header);

	// Place the object in the sprite
	sswf::TagPlace *place = new sswf::TagPlace(sprite);
	place->SetDepth(1);
	place->SetObjectID(shape->Identification());
	matrix.SetScale(1, 1);
	matrix.SetTranslate(bitmap.f_width * 10, bitmap.f_height * 10);
	place->SetMatrix(matrix);

	// Show the image (not required, but a good practice!)
	(void) new sswf::TagShowFrame(sprite);

	// End the sprite (just like a movie)
	(void) new sswf::TagEnd(sprite);

	// Now place the sprite in the main movie
	place = new sswf::TagPlace(my_header);
	place->SetDepth(1);
	place->SetObjectID(sprite->Identification());

	// Show the sprite (the very last one is not required,
	// but a good practice anyway!)
	(void) new sswf::TagShowFrame(my_header);

	// If more frames are added, we want to remove this
	// current frame first!
	remove_image = sprite->Identification();

	if(need_close) {
		save_movie(image_filename);
	}

	return;
}









void usage(void)
{
	fprintf(stderr, "jpg2swf version %s\n", version);
	fprintf(stderr, "Usage: %s [-opt] <image> ...\n", progname);
	fprintf(stderr, "where -opt is one of the following:\n");
	fprintf(stderr, "   -b <rgb>      specifies the color (#RRGGBB)\n");
	fprintf(stderr, "   -B            blank out the screen at the end if not looping\n");
	fprintf(stderr, "   -h            print out this help screen\n");
	fprintf(stderr, "   -l            whether the slideshow should loop\n");
	fprintf(stderr, "   -o <output>   set the output filename for the next input\n");
	fprintf(stderr, "   -q <quality>  defines the quality (0 to 100)\n");
	fprintf(stderr, "   -r <rate>     the playback rate (floating point of # of frames per second)\n");
	fprintf(stderr, "   -s            create a slideshow instead of a set of movies\n");
	exit(1);
}


int main(int argc, char *argv[])
{
	char *last_filename;

	progname = strrchr(argv[0], '/');
	if(progname == 0) {
		progname = argv[0];
	}
	else {
		progname++;
	}

	last_filename = 0;
	int i = 1;
	while(i < argc) {
		char *arg = argv[i];
		if(arg[0] == '-') {
			int j = 1;
			int l = strlen(arg);
			while(j < l) {
				switch(arg[j]) {
				case 'b':
					i++;
					if(i < argc) {
						// we expect '#RRGGBB'
						char *col = argv[i];
						if(*col == '#') {
							col++;
						}
						background_color = strtol(col, NULL, 16);
					}
					break;

				case 'B':
					blankout = 1;
					break;

				case 'h':
					usage();
					break;

				case 'l':
					loop = 1;
					break;

				case 'o':
					i++;
					if(i < argc) {
						output = argv[i];
					}
					break;

				case 'q':
					i++;
					if(i < argc) {
						quality = atol(argv[i]);
					}
					break;

				case 'r':
					i++;
					if(i < argc) {
						rate = atof(argv[i]);
					}
					break;

				case 's':
					create_movie(last_filename);
					last_filename = 0;
					remove_image = -1;
					break;

				default:
					fprintf(stderr, "ERROR: unknown option -%c\n", argv[i][j]);
					usage();
					break;

				}
				j++;
			}
		}
		else {
			last_filename = arg;
			convert_image(arg);
			if(my_header == 0) {
				output = 0;
			}
		}
		i++;
	}

	if(my_header != 0 && last_filename != 0) {
		save_movie(last_filename);
	}

	return 0;
}



