using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using NDesk.Options;

public class AutoStub
{
    private Bot bot;

    private int unchanged;
    private int upgrade;
    private int newgrade;
    private int downgrade;

    private int  max     = 1;
    private bool debugs  = false;
    private bool force   = false;
    private bool help    = false;
    private bool verbose = false;
    private string filename;

    private string checkpoint ()
    {
        StreamReader streamReader = null;
        string line = null;

        Console.WriteLine ("Checkpoint file is " + filename);
        if (File.Exists (filename))
        {
            try
            {
                streamReader = new StreamReader (filename);
                line = streamReader.ReadLine ();
            }
            catch (Exception e)
            {
                bot.Cred.Warning (e.Message);
            }
            finally
            {
                if (null != streamReader)
                {
                    streamReader.Close();
                }
            }
        }
        Console.WriteLine ("Checkpoint is " + (null == line ? "null" : line));
        return line;
    }

    private void checkpoint (string line)
    {
        StreamWriter streamWriter = null;
        try
        {
            streamWriter = new StreamWriter (filename);
            streamWriter.WriteLine (line);
        }
        catch (Exception e)
        {
            bot.Cred.Warning (e.Message);
        }
        finally
        {
            if (null != streamWriter)
            {
               streamWriter.Close ();
            }
        }
    }

    private bool compareClass (string oldClass, string botClass)
    {
        Dictionary<string, int> classes = new Dictionary<string, int>()
        {
            { "Stub",  0 },
            { "Start", 1 },
            { "List",  1 },
            { "C",     2 },
            { "CL",    2 },
            { "B",     3 },
            { "BL",    3 },
        };

        if (oldClass.Equals (""))
        {
            newgrade++;
            return true;
        }

        if (! classes.ContainsKey (oldClass))
        {
            throw new ApplicationException ("unknown class: '" + oldClass + "'");
        }

        int diff = classes[botClass] - classes[oldClass];
        if (0 > diff)
        {
            downgrade++;
        }
        else if (0 == diff)
        {
            unchanged++;
            return false;
        }
        else if (0 < diff)
        {
            upgrade++;
        }
        return true;
    }

    private void report ()
    {
        bot.Cred.Showtime (String.Format("{0} articles newly rated, {1} downgraded, {2} upgraded, {3} unchanged - total {4}",
                            newgrade, downgrade, upgrade, unchanged, newgrade + downgrade + upgrade + unchanged));
    }

    private void autoCheck (Page article, Page talk)
    {
        try
        {
            bot.Cred.Showtime (article.Title);
            article.Load ();
            talk.Load ();
            var template = talk.MilHist.ProjectTemplate;
            var oldClass = template.Class;
            var rating   = template.Rating;
            var botClass = rating.Class;

            Debug.WriteLine  ("\tOriginal: " + template.Text);
            template.RemoveAll (@"^class$|^importance$|^b\d$|^B-Class-\d$");
            template.Rating = rating;

            Debug.WriteLine ("\told rating = " + oldClass);
            Debug.WriteLine ("\tprediction = " + article.Prediction ());
            Debug.WriteLine ("\tbot rating = " + botClass);

            Debug.WriteLine ("\tModified: " + template.Text);
            var changed = compareClass (oldClass, botClass);
            if (changed && force)
            {
                talk.Save ("AutoStub: Automatic MILHIST checklist reassessment - " + botClass + " class");
                bot.Cred.Showtime ("\tChanged from " + oldClass + " to " + botClass);
            }
        }
        catch (ApplicationException ex)
        {
            string message = "Error in " + article.Title + ": " + ex.Message;
            bot.Cred.Showtime (message);
        }
        catch (Exception ex)
        {
            string message = "Error in " + article.Title + ":\n" + ex.Message + "\n" + ex.StackTrace;
            if (debugs)
            {
                Debug.WriteLine (message);
            }
            else
            {
                bot.Cred.Warning (message);
                bot.Close ();
            }
            Environment.Exit (1);
        }
    }

    private void autoCheck (List<Page> talkPages)
    {
        foreach (var talkPage in talkPages)
        {
            // Could be a subcategory
            if (talkPage.Namespace.Equals ("Talk"))
            {
                autoCheck (talkPage.Article, talkPage);
            }
        }
        report ();
    }

    private static void showHelp (OptionSet options)
    {
        Console.WriteLine ("Usage: mono AutoStub [OPTIONS]+ <article>");
        Console.WriteLine ("Assess article and update the MilHist template on the talk page.");
        Console.WriteLine ();
        Console.WriteLine ("Options:");
        options.WriteOptionDescriptions (Console.Out);
        Environment.Exit (0);
    }

    List<string> options (string [] args)
    {
        var optionSet = new OptionSet () {
            { "d|debug",   "debugging",    v => debugs  = v != null },
            { "f|force",   "update page",  v => force   = v != null },
            { "h|?|help",  "display help", v => help    = v != null },
            { "n|max=",    "number of pages to process",  v => int.TryParse (v, out max) },
            { "v|verbose", "vebosity",     v => verbose = v != null },
        };

        List<string> extras = optionSet.Parse (args);

        if (help)
        {
            showHelp (optionSet);
        }

        return extras;
    }

    private AutoStub (string [] args)
    {
        bot = new Bot ();
        Debug.Bot = bot;
        Debug.On  = debugs;
        filename  = Path.Combine (bot.Cred.LogsDirectory, bot.Cred.Job + ".txt");
        var articles = options (args);

        bot.Cred.Showtime ("started");
        if (articles.Count > 0)
        {
            var articlePage = new Page (bot, articles[0]);
            autoCheck (articlePage, articlePage.Talk);
        }
        else
        {
            int batchSize = 100;
            int batch = Math.Min (max, batchSize);
            var query = new Query ("Stub-Class military history articles", batch, checkpoint());
            do
            {
                var talkPages = bot.Category (query);
                checkpoint (query.Start);
                autoCheck (talkPages);
                max -= talkPages.Count;
                if (max < batch)
                {
                    query.Limit = max.ToString ();
                }
            } while (query.Continue && max > 0);

            if (max > 0)
            {
                File.Delete (filename);
            }
        }
         bot.Cred.Showtime ("done");
    }

    static public void Main (string [] args)
    {
        var autoStub = new AutoStub (args);
    }
}