/**
 * Copyright (c) 2006-2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 * 
 * Contributors: 
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.codegen.merge.properties;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This implements the method {@link #run}, 
 * which is called just like main during headless workbench invocation.
 */
public class PropertyMerger 
{
  protected String sourceProperties;
  protected String targetProperties;
  protected Map<String, String> sourceToTargetMap = new LinkedHashMap<String, String>();
  protected Map<String, String> targetToSourceMap = new LinkedHashMap<String, String>();

  /**
   * This creates an empty instances, when used as a runnable.
   */
  public PropertyMerger()
  {
    super();
  }

  public String getSourceProperties()
  {
    return sourceProperties;
  }

  public void setSourceProperties(String sourceProperties)
  {
    this.sourceProperties = sourceProperties;
  }

  public String getTargetProperties()
  {
    return targetProperties;
  }

  public void setTargetProperties(String targetProperties)
  {
    this.targetProperties = targetProperties;
  }

  public Map<String, String> getSourceToTargetMap()
  {
    return sourceToTargetMap;
  }

  /**
   * Create a JDOM from a URI.
   */
  public String createPropertiesForURI(String uri)
  {
    try
    {
      URL url = null;
      try
      {
        url = new URL(uri);
      }
      catch (MalformedURLException exception)
      {
        url = new URL("file:" + uri);
      }
      BufferedInputStream bufferedInputStream = new BufferedInputStream(url.openStream());
      byte [] input = new byte [bufferedInputStream.available()];
      bufferedInputStream.read(input);
      bufferedInputStream.close();
      return new String(input, "ISO-8859-1");
    }
    catch (IOException exception)
    {
      // Ignore
    }

    return null;
  }

  public String createPropertiesForInputStream(InputStream inputStream)
  {
    try
    {
      BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
      byte [] input = new byte [bufferedInputStream.available()];
      bufferedInputStream.read(input);
      bufferedInputStream.close();
      return new String(input, "ISO-8859-1");
    }
    catch (IOException exception)
    {
      // Ignore
    }
    return null;
  }

  protected static Pattern nlPattern = Pattern.compile("([\\n][\\r]?|[\\r][\\n]?)", Pattern.MULTILINE);
  
  public void merge()
  {
    Matcher matcher = nlPattern.matcher(targetProperties);
    String nl = null;
    if (matcher.find())
    {
      nl = matcher.group(1);  
      
      matcher = nlPattern.matcher(sourceProperties);
      if (matcher.find())
      {
        String sourceNL = matcher.group(1);
        if (!sourceNL.equals(nl))
        {
          sourceProperties = sourceProperties.replaceAll(sourceNL, nl);
        }
      }
    }
    else
    {
      matcher = nlPattern.matcher(sourceProperties);
      if (matcher.find())
      {
        nl = matcher.group(1);  
      }
    }
    
    if (nl != null)
    {
      if (!targetProperties.endsWith(nl))
      {
        targetProperties += nl;
      }
      
      if (!sourceProperties.endsWith(nl))
      {
        sourceProperties += nl;
      }
    }
    
    Map<String, String> sourcePropertyFragments = parse(sourceProperties);
    Map<String, String> targetPropertyFragments = parse(targetProperties);
    
    StringBuffer result = new StringBuffer(targetProperties);
    for (Map.Entry<String, String> entry : sourcePropertyFragments.entrySet())
    {
      if (!targetPropertyFragments.containsKey(entry.getKey()))
      {
        result.append(entry.getValue());
      }
    }

    targetProperties = result.toString();
  }

  protected static Pattern propertyLine = Pattern.compile("\\s*(\\S+)\\s*=.*", Pattern.MULTILINE);

  public Map<String, String> parse(String properties)
  {
    Map<String, String> result = new LinkedHashMap<String, String>();
    int i = 0;
    while (i < properties.length())
    {
      int eol;
      for (int start = i;;)
      {
        eol = properties.indexOf("\n", start);
        if (eol != -1)
        {
          if (eol + 1 < properties.length() && properties.charAt(eol + 1) == '\r')
          {
            if (eol > start && properties.charAt(eol - 1) == '\\')
            {
              start = eol + 2;
            }
            else
            {
              ++eol;
              break;
            }
          }
          else if (eol > start && properties.charAt(eol - 1) == '\\' ||
                     eol - 1 > start && properties.charAt(eol - 1) == '\r' && properties.charAt(eol - 2) == '\\')
          {
            start = eol + 1;
          }
          else
          {
            break;
          }
        }
        else
        {
          eol = properties.indexOf("\r", start);
          if (eol == -1)
          {
            eol = properties.length() - 1;
            break;
          }
          else if (eol > start && properties.charAt(eol - 1) == '\\')
          {
            start = eol + 1;
          }
          else
          {
            break;
          }
        }
      }

      String property = properties.substring(i, eol + 1);
      Matcher matcher = propertyLine.matcher(property);
      if (matcher.find() && matcher.groupCount() >= 1)
      {
        int begin = matcher.start(1);
        int end = matcher.end(1);
        String propertyName =  property.substring(begin, end);
        if (propertyName.indexOf("#") == -1)
        {
          result.put(propertyName, property);
        }
        else if (propertyName.startsWith("#"))
        {
          result.put(propertyName.substring(1), property);
        }
      }

      i = eol + 1;
    }

    return result;
  }


  /**
   * This is called with the command line arguments of a headless workbench invocation.
   */
  public Object run(Object object) 
  {
    try
    {
      // Three arguments are expected: the .xml jControlModel URI, the source java URI, and the target java URI.
      //
      String[] arguments = (String[])object;

      // Create the source and target JDOMs.
      //
      sourceProperties = createPropertiesForURI(arguments[0]);
      targetProperties = createPropertiesForURI(arguments[1]);

      merge();

      System.out.println("**********************************************");
      System.out.println(targetProperties);

      return 0;
    }
    catch (Exception exception)
    {
      return 1;
    }
  }
  
/////////////////////////////////  HEADLESS INVOCATION  /////////////////////////////////////

  @Deprecated
  public static class PlatformRunnable extends PropertyMerger
  {
    // Empty
  }
}
