/**
 *
 * Copyright INRA-URGI 2009-2010
 * 
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software. You can use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 * 
 * As a counterpart to the access to the source code and rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty and the software's author, the holder of the
 * economic rights, and the successive licensors have only limited
 * liability.
 * 
 * In this respect, the user's attention is drawn to the risks associated
 * with loading, using, modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean that it is complicated to manipulate, and that also
 * therefore means that it is reserved for developers and experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and, more generally, to use and operate it in the
 * same conditions as regards security.
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 *
 */
import java.util.*;
import java.io.File;
import java.io.*;
import java.util.regex.*;

public class PythonHelperReader {

  String         fileName;
  Program        program;
  BufferedReader reader;
  String         message;

  public PythonHelperReader(String fileName) {
    this.fileName = fileName;  
    this.reader   = reader;
    this.message  = null;
  }

  public void setReader(BufferedReader reader) {
    this.reader = reader;
  }
  
  public void run() {
    this.program                     = new Program();
    boolean         inBeginning      = true;
    boolean         inUsage          = false;
    boolean         afterUsage       = false;
    boolean         inDescription    = false;
    boolean         afterDescription = false;
    boolean         inOptions        = false;
    boolean         inOptionBlank    = false;
    boolean         inError          = false;
    String          usage            = null;
    String          description      = null;
    String          option           = null;
    Vector <String> options          = new Vector < String > ();
    String[]        optionSplitted;

    // Parse file
    try {
      String line = null;

      while ((line = reader.readLine()) != null) {
        line = line.trim();
        if (line.startsWith("Traceback")) {
          this.message     = "Problem with header of '" + this.fileName + "':\n" + line + "\n";
          inError          = true;
          inBeginning      = false;
          inUsage          = false;
          afterUsage       = false;
          inDescription    = false;
          afterDescription = false;
          inOptions        = false;
          inOptionBlank    = false;
        }
        else if (inError) {
          this.message += line + "\n";
        }
        else if (inBeginning) {
          if (line.startsWith("Usage:")) {
            inUsage     = true;
            inBeginning = false;
            usage       = line;
          }
        }
        else if (inUsage) {
          if ("".equals(line)) {
            inUsage    = false;
            afterUsage = true;
          }
          else {
            usage += " " + line;
          }
        }
        else if (afterUsage) {
          if (! "".equals(line)) {
            description   = line;
            afterUsage    = false;
            inDescription = true;
          }
        }
        else if (inDescription) {
          if ("".equals(line)) {
            inDescription    = false;
            afterDescription = true;
          }
          else {
            description += " " + line;
          }
        }
        else if (afterDescription) {
          if (! "".equals(line)) {
            afterDescription = false;
            inOptions        = true;
          }
        }
        else if (inOptions) {
          if ("".equals(line)) {
            inOptions     = false;
            inOptionBlank = true;
          }
          else {
            if (option == null) {
              option = line;
            }
            else {
              if (line.charAt(0) == '-') {
                options.add(option);
                option = line;
              }
              else {
                option += " " + line;
              }
            }
          }
        }
        else if (inOptionBlank) {
          if (! "".equals(line)) {
            inOptionBlank = false;
            inOptions     = true;
          }
        }
        else {
          this.message = "Something is wrong in the file '" + this.fileName + "'.";
          return;
        }
      }

      reader.close();
    }
    catch (FileNotFoundException e) {
      this.message = "File " + this.fileName + " not found";
      return;
    }
    catch (IOException e) {
      this.message = "IOException while reading file " + this.fileName;
      return;
    }

    if (inError) {
      return;
    }

    if (option != null) {
      options.add(option);
    }

    HashMap < String, ProgramOption > identifierToOptions = new HashMap < String, ProgramOption > ();
    HashMap < ProgramOption, String > associatedOption    = new HashMap < ProgramOption, String > ();

    if (usage == null) {
      this.message = "Cannot read the usage of file " + this.fileName;
      return;
    }
    program.setShortName(usage.split(" ")[1].trim());
    program.setName(description.split(":")[0].trim());

    Pattern pattern = Pattern.compile("\\[Category: .*\\]");
    Matcher matcher = pattern.matcher(description);
    if (matcher.find()) {
      program.setSection(description.substring(matcher.start() + "[Category: ".length(), matcher.end() - 1));
      program.setDescription(description.substring(0, matcher.start() - 1).trim());
    }
    else {
      this.message = "Cannot find category in description '" + description + "' in file " + this.fileName;
      return;
    }
    for (int i = 0; i < options.size(); i++) {
      option         = options.get(i).replace("\t", " ");
      optionSplitted = option.split(" ");
      option         = "";
      for (int j = 3; j < optionSplitted.length; j++) {
        option += optionSplitted[j] + " ";
      }
      
      String identifier = optionSplitted[0].replace("-", "").replace(",", "");
      // Skip -h and -v options
      if (("h".equals(identifier)) || ("v".equals(identifier)))
        continue;

      ProgramOption programOption = new ProgramOption();
      programOption.setIdentifier("-" + identifier);
      programOption.setComment(option.substring(0, option.indexOf("[")).trim());
      identifierToOptions.put(identifier, programOption);

      pattern = Pattern.compile("\\[[^\\]]*\\]");
      matcher = pattern.matcher(option);
      while (matcher.find()) {
        String inner = option.substring(matcher.start()+1, matcher.end()-1);
        if (inner.contains(":")) {
          String type  = inner.substring(0, inner.indexOf(":")).trim();
          String value = inner.substring(inner.indexOf(":")+1).trim();
          // Types of the options
          if ("format".compareToIgnoreCase(type) == 0) {
            String currentWord = "";
            String rest        = "";
            if (value.contains(" ")) {
              int pos     = value.indexOf(" ");
              currentWord = value.substring(0, pos);
              rest        = value.substring(pos+1);
            }
            else {
              currentWord = value;
            }
            // Output file type
            if ("output".compareToIgnoreCase(currentWord) == 0) {
              programOption.setInput(false);
              int pos     = rest.indexOf(" ");
              currentWord = rest.substring(0, pos).trim();
              rest        = rest.substring(pos+1).trim();
            }
            // File (input or output file)
            if ("file".compareToIgnoreCase(currentWord) == 0) {
              programOption.setType("file");
              // Format given by an associated option (to be found later)
              if (rest.startsWith("in format given by ")) {
                associatedOption.put(programOption, rest.substring(rest.indexOf("format given by ") + "format given by ".length() + 1).trim());
              }
              else {
                if (! rest.startsWith("in ")) {
                  this.message = "Descriptor " + option + " does not have a proper format.";
                  return;
                }
                rest = rest.substring("in ".length());
                int pos = rest.indexOf(" format");
                if (pos == -1) {
                  this.message = "Descriptor " + option + " does not have a proper format.";
                  return;
                }
                programOption.setFormat(rest.substring(0, pos).trim().toLowerCase().split(" or "));
              }
            }
            // Format type
            else if (rest.endsWith("file format")) {
              programOption.setFormat((currentWord + " " + rest.substring(0, rest.indexOf("file format"))).trim().toLowerCase().split(" or "));
              programOption.setType("format");
            }
            // Choice type
            else if ("choice".compareToIgnoreCase(currentWord) == 0) {
              programOption.setChoices(rest.replace("(", "").replace(")", "").split(", "));
              programOption.setType("choice");
            }
            // Boolean type
            else if ("bool".compareToIgnoreCase(currentWord) == 0) {
              programOption.setType("boolean");
            }
            // Other type
            else {
              programOption.setType(currentWord);
            }
          }
          // Default value
          else if ("default".compareToIgnoreCase(type) == 0) {
            programOption.setDefault(value);
          }
          else {
            this.message = "Do not understand option descriptor '" + inner + "'.";
            return;
          }
        }
        else {
          // Compulsory option
          if ("compulsory".compareToIgnoreCase(inner) == 0) {
            programOption.setCompulsory(true);
          }
          else {
            this.message = "Do not understand option descriptor '" + inner + "'.";
            return;
          }
        }
      }
      program.addOption(programOption);
    }

    // Set associated option
    Iterator it = associatedOption.keySet().iterator();
    while (it.hasNext()) {
      ProgramOption programOption = (ProgramOption) it.next();
      programOption.setAssociatedOption(identifierToOptions.get(associatedOption.get(programOption)));
    }
  }

  public String getMessage () {
    return this.message;
  }

  public Program getProgram () {
    return this.program;
  }
}


