//  
//   Rebex Sample Code License
// 
//   Copyright 2025, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   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.
// 

using System;
using System.IO;
using System.Text;

using Rebex.TerminalEmulation;

namespace Rebex.Samples
{
    /// <summary>
    /// The ANSI convertor class converts a text file containing ANSI escape sequences 
    /// to another format (image, HTML or plain text) using VirtualTerminal class. 
    /// ANSI escape sequences are interpreted by the VirtualTerminal class and
    /// rendered on the VirtualTerminal's screen. Thenn, the VirtualTerminal's screen
    /// is saved in the specified format.
    /// </summary>
    public class AnsiConvertor
    {
        /// <summary>Default terminal width in columns.</summary>
        private const int DEFAULT_TERMINAL_COLUMNS = 80;

        /// <summary>Default terminal height in rows.</summary>
        private const int DEFAULT_TERMINAL_ROWS = 25;

        /// <summary>
        /// Class that stores parsed program arguments.
        /// </summary>
        private class ProgramArguments
        {
            /// <summary>
            /// Gets or sets the path of the source file.
            /// </summary>
            public string SourcePath
            {
                get { return _sourcePath; }
                set { _sourcePath = value; }
            }
            private string _sourcePath;

            /// <summary>
            /// Gets or sets the path of the output file.
            /// </summary>
            public string OutputPath
            {
                get { return _outputPath; }
                set { _outputPath = value; }
            }
            private string _outputPath;

            /// <summary>
            /// Gets or sets the capture format of the output file.
            /// </summary>
            public TerminalCaptureFormat CaptureFormat
            {
                get { return _captureFormat; }
                set { _captureFormat = value; }
            }
            private TerminalCaptureFormat _captureFormat;

            /// <summary>
            /// Gets or sets the number of columns of the Virtual terminal.
            /// </summary>
            public int Columns
            {
                get { return _columns; }
                set { _columns = value; }
            }
            private int _columns;

            /// <summary>
            /// Gets or sets the number of rows of the Virtual terminal.
            /// </summary>
            public int Rows
            {
                get { return _rows; }
                set { _rows = value; }
            }
            private int _rows;
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        /// <param name="args">Program arguments.</param>
        /// <returns>Exit code.</returns>
        public static int Main(string[] args)
        {
            // set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key;


            // if bad number of arguments was specified, show help and return
            if (args.Length != 3 && args.Length != 5)
            {
                ShowHelp();
                return 1;
            }

            // get parsed program arguments
            ProgramArguments arguments = GetProgramArguments(args);

            // if program arguments were not parsed successfully, return
            if (arguments == null)
            {
                return 1;
            }

            // create a new virtual (off-screen) terminal
            VirtualTerminal terminal = new VirtualTerminal(arguments.Columns, arguments.Rows);

            // ANSI text file may contains a terminal screen size change sequence that triggers the RemoteResize event.
            // By default, this event is ignored, but because we wish support these screen size changes, we have to 
            // register the RemoteResize event handler and change the terminal screen size accordingly.
            if (args.Length == 3)
            {
                terminal.RemoteResize += terminal_RemoteResize;
            }

            try
            {
                // open the file for reading
                using (StreamReader reader = new StreamReader(arguments.SourcePath))
                {
                    // read the whole file to the string
                    string data = reader.ReadToEnd();

                    // write the data to the terminal screen
                    terminal.Screen.Write(data);
                }

                // save terminal to specified outputPath
                terminal.Save(arguments.OutputPath, arguments.CaptureFormat);

                Console.WriteLine("Successfully converted.");
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception occured: {0}", e);
                return 1;
            }

            return 0;
        }

        /// <summary>
        /// Handles the terminal RemoteResize event.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event arguments.</param>
        private static void terminal_RemoteResize(object sender, RemoteResizeEventArgs e)
        {
            VirtualTerminal terminal = sender as VirtualTerminal;
            if (terminal != null)
            {
                // set the size of the terminal screen according to the event values
                terminal.SetScreenSize(e.Columns, e.Rows);
            }
        }

        /// <summary>
        /// Shows syntax and examples.
        /// </summary>
        private static void ShowHelp()
        {
            Console.WriteLine("---------------------------------------------------------------------------------");
            Console.WriteLine("Rebex ANSI Convertor - converts ANSI text file to the file of 'another' format");
            Console.WriteLine("---------------------------------------------------------------------------------");
            Console.WriteLine("Formats available: Ansi, Text, Html, Bmp, Jpeg, Png, Gif, Tiff.");
            Console.WriteLine();
            Console.WriteLine("Syntax: AnsiConvertor sourcePath outputPath format [terminalColumns terminalRows]");
            Console.WriteLine("If terminalColumns and terminalRows are not specified explicitly, it will be taken");
            Console.WriteLine("from the source file if available. If terminal resolution is not stored within the file,");
            Console.WriteLine("the default values will be used ({0} for terminalColumns and {1} for terminalRows).",
                DEFAULT_TERMINAL_COLUMNS, DEFAULT_TERMINAL_ROWS);
            Console.WriteLine();
            Console.WriteLine("Example: AnsiConvertor ansiFile.ans imageFile.png png 80 25");
            Console.WriteLine("Example: AnsiConvertor ansiFile.ans imageFile.png png");
        }

        /// <summary>
        /// Parses program arguments and returns them in the ProgramArguments class.
        /// </summary>
        /// <param name="args">Program arguments.</param>
        /// <returns>
        /// Initialized ProgramArguments class filled with parsed program arguments;
        /// null if the arguments can't be parsed.
        /// </returns>
        private static ProgramArguments GetProgramArguments(string[] args)
        {
            ProgramArguments arguments = new ProgramArguments();

            arguments.SourcePath = args[0];
            arguments.OutputPath = args[1];

            // check whether the source file exist
            if (!File.Exists(arguments.SourcePath))
            {
                Console.WriteLine("Specified input file ({0}) doesn't exist.", arguments.SourcePath);
                return null;
            }

            // parse TerminalCaptureFormat
            switch (args[2].ToLower())
            {
                case "bmp": arguments.CaptureFormat = TerminalCaptureFormat.Bmp; break;
                case "gif": arguments.CaptureFormat = TerminalCaptureFormat.Gif; break;
                case "htm":
                case "html": arguments.CaptureFormat = TerminalCaptureFormat.Html; break;
                case "jpg":
                case "jpeg": arguments.CaptureFormat = TerminalCaptureFormat.Jpeg; break;
                case "png": arguments.CaptureFormat = TerminalCaptureFormat.Png; break;
                case "txt":
                case "text": arguments.CaptureFormat = TerminalCaptureFormat.Text; break;
                case "tif":
                case "tiff": arguments.CaptureFormat = TerminalCaptureFormat.Tiff; break;
                case "ans":
                case "ansi": arguments.CaptureFormat = TerminalCaptureFormat.Ansi; break;
                default:
                    ShowHelp();
                    Console.WriteLine();
                    Console.WriteLine("Unrecognized output format ({0}).", args[2]);
                    return null;
            }

            // parse terminalColumns and terminalRows if specified
            if (args.Length == 5)
            {
                try { arguments.Columns = int.Parse(args[3]); }
                catch { arguments.Columns = 0; }

                if (arguments.Columns <= 0)
                {
                    Console.WriteLine("TerminalColumns must be positive number, but was ({0}).", args[3]);
                    return null;
                }
                
                try { arguments.Rows = int.Parse(args[4]); }
                catch { arguments.Rows = 0; }

                if (arguments.Rows <= 0)
                {
                    Console.WriteLine("TerminalRows must be positive number, but was ({0}).", args[4]);
                    return null;
                }
            }
            else
            {
                arguments.Columns = DEFAULT_TERMINAL_COLUMNS;
                arguments.Rows = DEFAULT_TERMINAL_ROWS;
            }

            return arguments;
        }
    }
}
