using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using libsbmlcs;

namespace ConsoleApplication1
{
    public class TestConstructors
    {
        private static bool HasLevelAndVersionConstructor(Type type)
        {
            var constructors = type.GetConstructors();

            foreach (var item in constructors)
            {
                var parameters = item.GetParameters();
                if (parameters.Length == 2 && parameters[0].ParameterType == typeof(long)
                    && parameters[1].ParameterType == typeof(long)) return true;
            }
            return false;
        }

        private static List<Type> FilterTypes(List<Type> result)
        {
            for (int i = result.Count - 1; i >= 0; i--)
            {
                if (!HasLevelAndVersionConstructor(result[i]))
                    result.RemoveAt(i);
            }
            return result;
        }
        private static List<Type> FindConstructorsWithLevelAndVersion()
        {
            var assembly = typeof(libsbml).Assembly;
            var allTypes = assembly.GetExportedTypes();
            var result = new List<Type>();
            result.AddRange(allTypes);


            return FilterTypes(result);
        }

        private static string TestLevelVersionConstructor(Type item, int level, int version)
        {
            bool exceptionOccurred = false;
            bool wrapped = false;
            var result = new StringBuilder();
            Exception ex1 = null;
            try
            {
                Activator.CreateInstance(item, level, version);
            }
            catch (Exception ex)
            {
                exceptionOccurred = true;
                wrapped = (ex.InnerException != null && ex.InnerException is SBMLConstructorException);
                ex1 = ex.InnerException;
            }

            if (exceptionOccurred && !wrapped)
            {
                result.AppendLine(String.Format("\tCreate L{0}V{1}", level, version));
                result.AppendLine(String.Format("\tFailed to create L{0}V{1} for {2}", level, version, item));
                if (ex1 != null)
                {
                    result.AppendLine(String.Format("\tWARNING: This exception is NOT wrapped correctly. Type is: {0}", ex1.GetType()));
                    result.AppendLine();
                }
            }

            if (!exceptionOccurred && level > 3)
            {
                result.AppendLine(String.Format("\tCreate L{0}V{1}", level, version));
                result.AppendLine("\tWARNING: Exception should have been thrown.");
                result.AppendLine();
                
            }
            return result.ToString();

        }
        private static void TestType(Type item)
        {
            var m1 = TestLevelVersionConstructor(item, 3, 1);
            var m2 = TestLevelVersionConstructor(item, 2, 3);
            var m3 = TestLevelVersionConstructor(item, 10, 10);

            if (!string.IsNullOrWhiteSpace(m1) ||
                !string.IsNullOrWhiteSpace(m2) ||
                !string.IsNullOrWhiteSpace(m3))
            {
                Console.WriteLine("Testing Type: {0}", item);
                Console.WriteLine("=============================================");
                if (!string.IsNullOrWhiteSpace(m1))
                Console.WriteLine(m1);
                if (!string.IsNullOrWhiteSpace(m2))
                Console.WriteLine(m2);
                if (!string.IsNullOrWhiteSpace(m3))
                Console.WriteLine(m3);
            }

        }

        private static void TestClasses(IEnumerable<Type> classes)
        {
            foreach (var item in classes)
            {
                TestType(item);
            }
        }

        public static void Main(string[] args)
        {
            var classes = FindConstructorsWithLevelAndVersion();

            TestClasses(classes);

            WriteCSFile("testConstructors.cs", classes);
            WriteJavaFile("testConstructors.java", classes);
            WriteRBFile("testConstructors.rb", classes);
            WritePLFile("testConstructors.pl", classes);
            WritePYFile("testConstructors.py", classes);

        }

        private static void WritePYFile(string fileName, IEnumerable<Type> classes)
        {
            var versions = new List<Tuple<int, int>> { new Tuple<int, int>(3, 1), new Tuple<int, int>  (2, 4), new Tuple<int, int> (10, 10)};

            var builder = new StringBuilder();

            builder.AppendLine("#!/usr/bin/env python"); 
            builder.AppendLine("#"); 
            builder.AppendLine("# This file has been autogenerated to test all constructors."); 
            builder.AppendLine("# "); 
            builder.AppendLine("import sys"); 
            builder.AppendLine("import time");
            builder.AppendLine("from libsbml import *"); 
            builder.AppendLine("");

            foreach (var item in classes)
            {
                builder.AppendLine(string.Format("print 'Testing: {0}'", item.Name));

                foreach (var combo in versions)
                {
                    builder.AppendLine("try:");
                    builder.AppendLine(string.Format("\ta = {0}({1}, {2})", item.Name, combo.Item1, combo.Item2));
                    builder.AppendLine("except:");
                    builder.AppendLine(string.Format("\tprint '  Failed to create L{0}V{1} for {2}'", combo.Item1, combo.Item2, item.Name));
                }

            }


            File.WriteAllText(fileName, builder.ToString());
        }

        private static void WritePLFile(string fileName, IEnumerable<Type> classes)
        {
            var versions = new List<Tuple<int, int>> { new Tuple<int, int>(3, 1), new Tuple<int, int>  (2, 4), new Tuple<int, int> (10, 10)};

            var builder = new StringBuilder();

            builder.AppendLine("#!/usr/bin/env perl"); 
            builder.AppendLine("#"); 
            builder.AppendLine("# This file has been autogenerated to test all constructors."); 
            builder.AppendLine("# ");
            builder.AppendLine("use LibSBML;"); 
            builder.AppendLine("");

            foreach (var item in classes)
            {
                builder.AppendLine(string.Format("print (\"Testing: {0}\\n\");", item.Name));

                foreach (var combo in versions)
                {
                    builder.AppendLine("eval {");
                    builder.AppendLine(string.Format("\t$a = new LibSBML::{0}({1}, {2});", item.Name, combo.Item1, combo.Item2));
                    builder.AppendLine("};");
                    builder.AppendLine("if ($@) {");
                    builder.AppendLine(string.Format("\tprint (\"  Failed to create L{0}V{1} for {2}\\n\");", combo.Item1, combo.Item2, item.Name));
                    builder.AppendLine("} ");
                }

            }


            File.WriteAllText(fileName, builder.ToString());
        }

        private static void WriteRBFile(string fileName, IEnumerable<Type> classes)
        {
            var versions = new List<Tuple<int, int>> { new Tuple<int, int>(3, 1), new Tuple<int, int>  (2, 4), new Tuple<int, int> (10, 10)};

            var builder = new StringBuilder();

            builder.AppendLine("#!/usr/bin/env ruby"); 
            builder.AppendLine("#"); 
            builder.AppendLine("# This file has been autogenerated to test all constructors."); 
            builder.AppendLine("# ");
            builder.AppendLine("require 'libSBML'"); 
            builder.AppendLine("");

            foreach (var item in classes)
            {
                builder.AppendLine(string.Format("puts 'Testing: {0}'", item.Name));

                foreach (var combo in versions)
                {
                    builder.AppendLine("begin");
                    builder.AppendLine(string.Format("\ta = LibSBML::{0}.new({1}, {2})", item.Name, combo.Item1, combo.Item2));
                    builder.AppendLine("rescue");
                    builder.AppendLine(string.Format("\tputs '  Failed to create L{0}V{1} for {2}'", combo.Item1, combo.Item2, item.Name));
                    builder.AppendLine("end");
                }

            }


            File.WriteAllText(fileName, builder.ToString());
        }

        private static void WriteJavaFile(string fileName, IEnumerable<Type> classes)
        {
            var versions = new List<Tuple<int, int>> { new Tuple<int, int>(3, 1), new Tuple<int, int>(2, 4), new Tuple<int, int>(10, 10) };

            var builder = new StringBuilder();

            builder.AppendLine("//");
            builder.AppendLine("//");
            builder.AppendLine("// This file has been autogenerated to test all constructors.");
            builder.AppendLine("// ");
            builder.AppendLine("");
            builder.AppendLine("import org.sbml.libsbml.*;");
            builder.AppendLine("");
            builder.AppendLine("public class testConstructors {");
            builder.AppendLine("  public static void main(String[] args) {");
            builder.AppendLine("    System.loadLibrary(\"sbmlj\");");

            foreach (var item in classes)
            {
                builder.AppendLine(string.Format("  System.out.println(\"Testing: {0}\");", item.Name));

                foreach (var combo in versions)
                {
                    builder.AppendLine("  try {");
                    builder.AppendLine(string.Format("    new {0}({1}, {2});", item.Name, combo.Item1, combo.Item2));
                    builder.AppendLine("  } catch (java.lang.Exception ex) {");
                    builder.AppendLine(string.Format("    System.out.println(\"  Failed to create L{0}V{1} for {2}\");", combo.Item1, combo.Item2, item.Name));
                    builder.AppendLine("  } ");
                }

            }
            builder.AppendLine("  } ");
            builder.AppendLine("}");

            File.WriteAllText(fileName, builder.ToString());
        }

        private static void WriteCSFile(string fileName, IEnumerable<Type> classes)
        {
            var versions = new List<Tuple<int, int>> { new Tuple<int, int>(3, 1), new Tuple<int, int>  (2, 4), new Tuple<int, int> (10, 10)};

            var builder = new StringBuilder();

            builder.AppendLine("//"); 
            builder.AppendLine("//"); 
            builder.AppendLine("// This file has been autogenerated to test all constructors."); 
            builder.AppendLine("// "); 
            builder.AppendLine("");
            builder.AppendLine("using libsbmlcs;");
            builder.AppendLine("using System;");
            builder.AppendLine("");
            builder.AppendLine("public class TestConstructors {");
            builder.AppendLine("  public static void Main(string[] args) {");

            foreach (var item in classes)
            {
                builder.AppendLine(string.Format("  Console.WriteLine(\"Testing: {0}\");", item.Name));

                foreach (var combo in versions)
                {
                    builder.AppendLine("  try {");
                    builder.AppendLine(string.Format("    new {0}({1}, {2});", item.Name, combo.Item1, combo.Item2));
                    builder.AppendLine("  } catch {");
                    builder.AppendLine(string.Format("    Console.WriteLine(\"  Failed to create L{0}V{1} for {2}\");", combo.Item1, combo.Item2, item.Name));
                    builder.AppendLine("  } ");
                }

            }
            builder.AppendLine("  } ");
            builder.AppendLine("}");

            File.WriteAllText(fileName, builder.ToString());
        }
    }
}
