//! \file       Program.cs
//! \date       Mon Jun 30 20:12:13 2014
//! \brief      game resources browser.
//

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using GameRes;

namespace GARbro
{
    class ConsoleBrowser
    {
        private string m_arc_name;

        void ListFormats ()
        {
            Console.WriteLine ("Recognized resource formats:");
            foreach (var impl in FormatCatalog.Instance.ArcFormats)
            {
                Console.WriteLine ("{0,-4} {1}", impl.Tag, impl.Description);
            }
        }

        void ExtractAll (ArcFile arc)
        {
            arc.ExtractFiles ((i, entry, msg) => {
                if (null != entry)
                {
                    Console.WriteLine ("Extracting {0} ...", entry.Name);
                }
                else if (null != msg)
                {
                    Console.WriteLine (msg);
                }
                return ArchiveOperation.Continue;
            });
        }

        void ExtractFile (ArcFile arc, string name)
        {
            Entry entry = arc.Dir.FirstOrDefault (e => e.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
            if (null == entry)
            {
                Console.Error.WriteLine ("'{0}' not found within {1}", name, m_arc_name);
                return;
            }
            Console.WriteLine ("Extracting {0} ...", entry.Name);
            arc.Extract (entry);
        }
        void TestArc (string[] args)
        {
/*
            if (args.Length > 1)
            {
                uint pass = GameRes.Formats.IntOpener.EncodePassPhrase (args[1]);
                Console.WriteLine ("{0:X8}", pass);
            }
*/
        }
        void Run (string[] args)
        {
            int argn = 0;
            if (args[argn].Equals ("-l"))
            {
                ListFormats();
                return;
            }
            if (args[argn].Equals ("-t"))
            {
                TestArc (args);
                return;
            }
            if (args[argn].Equals ("-x"))
            {
                ++argn;
                if (args.Length < 2)
                {
                    Usage();
                    return;
                }
            }
            DeserializeGameData();
            foreach (var file in VFS.GetFiles (args[argn]))
            {
                m_arc_name = file.Name;
                var arc = ArcFile.TryOpen (file);
                if (null == arc)
                {
                    Console.Error.WriteLine ("{0}: unknown format", m_arc_name);
                    continue;
                }
                using (arc)
                {
                    if (args.Length > argn+1)
                    {
                        for (int i = argn+1; i < args.Length; ++i)
                            ExtractFile (arc, args[i]);
                    }
                    else if (args[0].Equals ("-x"))
                    {
                        ExtractAll (arc);
                    }
                    else
                    {
                        foreach (var entry in arc.Dir.OrderBy (e => e.Offset))
                        {
                            Console.WriteLine ("{0,9} [{2:X8}] {1}", entry.Size, entry.Name, entry.Offset);
                        }
                    }
                }
            }
        }

        void DeserializeGameData ()
        {
            string scheme_file = Path.Combine (FormatCatalog.Instance.DataDirectory, "Formats.dat");
            try
            {
                using (var file = File.OpenRead (scheme_file))
                    FormatCatalog.Instance.DeserializeScheme (file);
            }
            catch (Exception X)
            {
                Console.Error.WriteLine ("Scheme deserialization failed: {0}", X.Message);
            }
        }

        static void Usage ()
        {
            Console.WriteLine ("Usage: gameres [OPTIONS] ARC [ENTRIES]");
            Console.WriteLine ("    -l   list recognized archive formats");
            Console.WriteLine ("    -x   extract all files");
            Console.WriteLine ("Without options displays contents of specified archive.");
        }

        static void Main (string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            if (0 == args.Length)
            {
                Usage();
                return;
            }
            var listener = new TextWriterTraceListener (Console.Error);
            Trace.Listeners.Add(listener);
            try
            {
                var browser = new ConsoleBrowser();
                browser.Run (args);
            }
            catch (Exception X)
            {
                Console.Error.WriteLine (X.Message);
            }
        }
    }
}
