///////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Academy of Motion Picture Arts and Sciences 
// ("A.M.P.A.S."). Portions contributed by others as indicated.
// All rights reserved.
// 
// A worldwide, royalty-free, non-exclusive right to copy, modify, create
// derivatives, and use, in source and binary forms, is hereby granted, 
// subject to acceptance of this license. Performance of any of the 
// aforementioned acts indicates acceptance to be bound by the following 
// terms and conditions:
//
//  * Copies of source code, in whole or in part, must retain the 
//    above copyright notice, this list of conditions and the 
//    Disclaimer of Warranty.
//
//  * Use in binary form must retain the above copyright notice, 
//    this list of conditions and the Disclaimer of Warranty in the
//    documentation and/or other materials provided with the distribution.
//
//  * Nothing in this license shall be deemed to grant any rights to 
//    trademarks, copyrights, patents, trade secrets or any other 
//    intellectual property of A.M.P.A.S. or any contributors, except 
//    as expressly stated herein.
//
//  * Neither the name "A.M.P.A.S." nor the name of any other 
//    contributors to this software may be used to endorse or promote 
//    products derivative of or based on this software without express 
//    prior written permission of A.M.P.A.S. or the contributors, as 
//    appropriate.
// 
// This license shall be construed pursuant to the laws of the State of 
// California, and any disputes related thereto shall be subject to the 
// jurisdiction of the courts therein.
//
// Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND 
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 
// FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO 
// EVENT SHALL A.M.P.A.S., OR ANY CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE 
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, RESITUTIONARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
//
// WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY 
// SPECIFICALLY DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER 
// RELATED TO PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY 
// COLOR ENCODING SYSTEM, OR APPLICATIONS THEREOF, HELD BY PARTIES OTHER 
// THAN A.M.P.A.S., WHETHER DISCLOSED OR UNDISCLOSED.
///////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//
//	exrdpx -- an OpenEXR/DPX file converter
//
//-----------------------------------------------------------------------------

#include <dpxToExr.h>
#include <exrToDpx.h>
#include <ImfTestFile.h>
#include <ImfThreading.h>
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <exception>

using namespace std;
using namespace Imf;

namespace {

void
usageMessage (const char argv0[], bool verbose = false)
{
    cerr << "usage: " << argv0 << " [options] dpxFile exrFile" << endl;
    cerr << "or:    " << argv0 << " [options] exrFile dpxFile" << endl;

    if (verbose)
    {
	cerr << "\n"
		"Converts an OpenEXR file into a DPX file\n"
		"or a DPX file into an OpenEXR file.\n"
		"\n"
		"Options:\n"
		"\n"
		"-C s   CTL transform s is applied to the input pixels\n"
		"       before the output file is written.  Option -C can\n"
		"       be specified multiple times to apply a series of\n"
		"       transforms to the pixels.  The transforms are\n"
		"       applied in the order in which they appear on the\n"
		"       command line.\n"
		"\n"
		"-z x   sets the data compression for the OpenEXR output\n"
		"       file to x (none/rle/zip/piz/pxr24/b44, default\n"
		"       is piz)\n"
		"\n"
		"-xyz   the OpenEXR output file will contain CIE XYZ\n"
		"       instead of Rec. 709 pixel data\n"
		"\n"
		"-yc    the pixels in the OpenEXR output file will be\n"
		"       in luminance/chroma instead of RGB format\n"
		"\n"
		"-s     the header of the DPX input file is checked less\n"
		"       strictly; some fields are ignored even though they\n"
		"       are part of the core set.\n"
		"\n"
		"-t n	process the pixels using n parallel threads\n"
		"\n"
		"-v     verbose mode\n"
		"\n"
		"-h     prints this message\n"
		"\n"
		"CTL transforms:\n"
		"\n"
		"       If one or more CTL transforms are specified on\n"
		"       the command line (using the -C flag), then those\n"
		"       transforms are applied to the input pixels.\n"
		"       If no CTL transforms are specified then a default\n"
		"       transform is applied.  The name of the default\n"
		"       transform is \"transform_EXR_DPX\" for OpenEXR to\n"
		"       DPX conversion and \"transform_DPX_EXR\" for DPX to\n"
		"       OpenEXR conversion.\n"
		"       The files that contain the transforms are located\n"
		"       using the CTL_MODULE_PATH environment variable.\n";

	 cerr << endl;
    }

    exit (1);
}


Compression
getCompression (const string &str)
{
    Compression c;

    if (str == "no" || str == "none" || str == "NO" || str == "NONE")
    {
	c = NO_COMPRESSION;
    }
    else if (str == "rle" || str == "RLE")
    {
	c = RLE_COMPRESSION;
    }
    else if (str == "zip" || str == "ZIP")
    {
	c = ZIP_COMPRESSION;
    }
    else if (str == "piz" || str == "PIZ")
    {
	c = PIZ_COMPRESSION;
    }
    else if (str == "pxr24" || str == "PXR24")
    {
	c = PXR24_COMPRESSION;
    }
    else if (str == "b44" || str == "B44")
    {
	c = B44_COMPRESSION;
    }
    else
    {
	cerr << "Unknown compression method \"" << str << "\"." << endl;
	exit (1);
    }

    return c;
}

} // namespace

int
main(int argc, char **argv)
{
    const char *fileName1 = 0;
    const char *fileName2 = 0;
    vector<string> transformNames;
    bool outputXyz = false;
    bool outputYc = false;
    bool strict = true;
    Compression compression = PIZ_COMPRESSION;
    int numThreads = 0;
    bool verbose = false;
    
    //
    // Parse the command line.
    //

    if (argc < 2)
	usageMessage (argv[0], true);

    int i = 1;
    int j = 1;

    while (i < argc)
    {
	if (!strcmp (argv[i], "-C"))
	{
	    //
	    // Apply a CTL transform
	    //

	    if (i > argc - 2)
		usageMessage (argv[0]);

	    transformNames.push_back (argv[i + 1]);
	    i += 2;
	}
	else if (!strcmp (argv[i], "-z"))
	{
	    //
	    // Select compression method
	    //

	    if (i > argc - 2)
		usageMessage (argv[0]);

	    compression = getCompression (argv[i + 1]);
	    i += 2;
	}
	else if (!strcmp (argv[i], "-xyz"))
	{
	    //
	    // Output OpenEXR with CIE XYZ primaries
	    //

	    outputXyz = true;
	    i += 1;
	}
	else if (!strcmp (argv[i], "-yc"))
	{
	    //
	    // Output OpenEXR in luminance/chroma format
	    //

	    outputYc = true;
	    i += 1;
	}
	else if (!strcmp (argv[i], "-t"))
	{
	    //
	    // Set number of threads
	    //

	    if (i > argc - 2)
		usageMessage (argv[0]);

	    numThreads = strtol (argv[i + 1], 0, 0);

	    if (numThreads < 0)
	    {
		cerr << "Number of threads cannot be negative." << endl;
		return 1;
	    }

	    i += 2;
	}
	else if (!strcmp (argv[i], "-v"))
	{
	    //
	    // Verbose mode
	    //

	    verbose = true;
	    i += 1;
	}
	else if (!strcmp (argv[i], "-s"))
	{
	    //
	    // Less strict DPX header checking
	    //

	    strict = false;
	    i += 1;
	}
	else if (!strcmp (argv[i], "-h"))
	{
	    //
	    // Print help message
	    //

	    usageMessage (argv[0], true);
	}
	else
	{
	    //
	    // image file name
	    //

	    if (j == 1)
		fileName1 = argv[i];
	    else if (j == 2)
		fileName2 = argv[i];
	    else
		usageMessage (argv[0]);

	    i += 1;
	    j += 1;
	}
    }

    if (fileName1 == 0 || fileName2 == 0)
	usageMessage (argv[0]);

    //
    // Do the work
    //

    int exitStatus = 0;

    try
    {
	setGlobalThreadCount (numThreads);

	if (isOpenExrFile (fileName1))
	{
	    if (transformNames.empty())
		transformNames.push_back ("transform_EXR_DPX");

	    exrToDpx (fileName1,
		      fileName2,
		      transformNames,
		      verbose);
	}
	else
	{
	    if (transformNames.empty())
		transformNames.push_back ("transform_DPX_EXR");

	    dpxToExr (fileName1,
		      fileName2,
		      transformNames,
		      outputXyz,
		      outputYc,
		      compression,
		      strict,
		      verbose);
	}
    }
    catch (const exception &e)
    {
	cerr << e.what() << endl;
	exitStatus = 1;
    }

    return exitStatus;
}
