/*-------------------------------------------------------------------------
Compiler Generator Coco/R,
Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
extended by M. Loeberbauer & A. Woess, Univ. of Linz
with improvements by Pat Terry, Rhodes University

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

As an exception, it is allowed to write an extension of Coco/R that is
used as a plugin in non-free software.

If not otherwise stated, any source code generated by Coco/R (other than
Coco/R itself) does not fall under the GNU General Public License.
-------------------------------------------------------------------------*/

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

namespace at.jku.ssw.Coco
{
    public static partial class Coco
    {
        public static GenerationResult Generate(GeneratorOptions options)
        {
            if (options == null)
                throw new ArgumentNullException(nameof(options));
            if (options.Grammar == null)
                throw new ArgumentException("The grammar is required.", nameof(options));
            if (options.ParserFrame == null)
                throw new ArgumentException("The parser frame is required.", nameof(options));
            if (options.GenerateScanner && options.ScannerFrame == null)
                throw new ArgumentException(
                    "The scanner frame is required when scanner generation is enabled.",
                    nameof(options)
                );

            var trace = new StringWriter();
            try
            {
                using var grammar = new MemoryStream(Encoding.UTF8.GetBytes(options.Grammar));
                var scanner = new Scanner(grammar);
                var parser = new Parser(scanner) { trace = trace };

                parser.tab = new Tab(parser)
                {
                    srcName = options.SourceName ?? "grammar.atg",
                    srcDir = "",
                    nsName = options.Namespace,
                };
                parser.dfa = new DFA(parser)
                {
                    Frame = options.ScannerFrame,
                    FrameName = "Scanner.frame",
                };
                parser.pgen = new ParserGen(parser)
                {
                    DirectDebug = options.DirectDebug,
                    Frame = options.ParserFrame,
                    FrameName = options.ParserFrameName ?? "Parser.frame",
                };

                if (options.DirectDebugTrace != null)
                    parser.tab.SetDDT(options.DirectDebugTrace);

                parser.errors.WriteMessage = options.WriteMessage;
                parser.errors.WriteError = options.WriteError;
                parser.errors.realFile = options.SourceName ?? "grammar.atg";

                parser.Parse();

                options.WriteMessage($"{parser.errors.count} errors detected");
                return new GenerationResult(
                    parser.errors.count == 0,
                    parser.pgen.GeneratedSource,
                    options.GenerateScanner ? parser.dfa.GeneratedSource : null,
                    trace.ToString()
                );
            }
            catch (IOException ex)
            {
                options.WriteError(ex.Message);
            }
            catch (FatalError ex)
            {
                options.WriteError(ex.Message);
            }

            return new GenerationResult(false, null, null, trace.ToString());
        }
    }
}
