/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.internal.gosu.properties;

import gw.fs.IFile;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.ITypeLoader;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeBase;
import gw.lang.reflect.gs.IPropertiesType;
import gw.util.GosuClassUtil;
import gw.util.concurrent.LockingLazyVar;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * Type based on a {@link PropertyNode}
 */
@SuppressWarnings("serial")
public class PropertiesType extends TypeBase implements IPropertiesType {

  private final PropertiesTypeLoader _typeLoader;
  private final PropertyNode _propertyNode;
  private IFile _file;
  private String _contentCached;
  private final LockingLazyVar<PropertiesTypeInfo> _typeInfo = new LockingLazyVar<PropertiesTypeInfo>() {
    @Override
    protected PropertiesTypeInfo init() {
      return new PropertiesTypeInfo(PropertiesType.this);
    }
  };
  private final LockingLazyVar<List<IPropertiesType>> _innerClasses = new LockingLazyVar<List<IPropertiesType>>() {
    @Override
    protected List<IPropertiesType> init() {
      List<IPropertiesType> innerClasses = new ArrayList<IPropertiesType>();
      for( IPropertyInfo pi: getTypeInfo().getProperties() )
      {
        IType type = pi.getFeatureType();
        if( type instanceof IPropertiesType )
        {
          innerClasses.add( (IPropertiesType)type );
        }
      }
      return innerClasses;
    }
  };

  public PropertiesType(PropertiesTypeLoader typeLoader, PropertyNode propertyNode, IFile file) {
    _typeLoader = typeLoader;
    _propertyNode = propertyNode;
    _file = file;
  }

  @Override
  public IType[] getInterfaces() {
    return EMPTY_TYPE_ARRAY;
  }

  @Override
  public int getModifiers()
  {
    return Modifier.PUBLIC | (!_propertyNode.isRoot() ? Modifier.STATIC : 0);
  }

  @Override
  public String getName() {
    return _propertyNode.getTypeName();  
  }

  @Override
  public String getNamespace() {
    return GosuClassUtil.getPackage( getName() );
  }

  @Override
  public String getRelativeName() {
    return GosuClassUtil.getShortClassName( getName() );
  }

  @Override
  public IType getSupertype() {
    return null;
  }

  @Override
  public ITypeInfo getTypeInfo() {
    return _typeInfo.get();
  }

  @Override
  public ITypeLoader getTypeLoader() {
    return _typeLoader;
  }

  @Override
  public boolean isMutable() {
    return false;
  }

  PropertyNode getPropertyNode() {
    return _propertyNode;
  }

  @Override
  public String getPropertiesFileKey( IPropertyInfo pi )
  {
    PropertiesPropertyInfo ppi = (PropertiesPropertyInfo)pi;
    return ppi.getPropertyEntryName();
  }

  @Override
  public IFile[] getSourceFiles() {
    if( _file == null ) {
      // _file may be null e.g., for SystemProperties
      return IFile.EMPTY_ARRAY;
    }
    return new IFile[] {_file};
  }

  public int findOffsetOf( PropertyNode node )
  {
    String fqn = node.getPath();
    // this is a crappy way to approximate the offset, we really need to parse the file ourselves and store the offsets
    return getCachedContent().indexOf( fqn );
  }

  public String getCachedContent()
  {
    if( _contentCached == null )
    {
      IFile[] files = getSourceFiles();
      if( files != null && files.length > 0 )
      {
        try
        {
          StringBuilder sb = new StringBuilder();
          List<String> lines = Files.readAllLines( files[0].toJavaFile().toPath(), Charset.defaultCharset() );
          for( String line: lines )
          {
            sb.append( line ).append( "\n" );
          }
          _contentCached = sb.toString();
            //new Scanner( files[0].toJavaFile() ).useDelimiter( "\\Z" ).next();
        }
        catch( Exception e )
        {
          throw new RuntimeException( e );
        }
      }
    }
    return _contentCached;
  }

  @Override
  public IType getInnerClass( CharSequence strTypeName )
  {
    for( IPropertiesType innerClass: _innerClasses.get() )
    {
      if( innerClass.getRelativeName().equals( strTypeName ) )
      {
        return innerClass;
      }
    }
    return null;
  }

  @Override
  public List<? extends IType> getInnerClasses()
  {
    return _innerClasses.get();
  }

  @Override
  public List<? extends IType> getLoadedInnerClasses()
  {
    return _innerClasses.get();
  }
}

@@ -379,16 +379,43 @@
 ssUtil;%0A
+import gw.util.StreamUtil;%0A
 import g
@@ -465,27 +465,22 @@
 ava.
-n
 io.
-charset.Charset
+InputStream
 ;%0Aim
@@ -493,22 +493,28 @@
 ava.
-n
 io.
-file.Files
+InputStreamReader
 ;%0Aim
@@ -3516,384 +3516,179 @@
     
-StringBuilder sb = new StringBuilder();%0A          List%3CString%3E lines = Files.readAllLines( files%5B0%5D.toJavaFile().toPath(), Charset.defaultCharset() );%0A          for( String line: lines )%0A          %7B%0A            sb.append
+InputStream inputStream = files%5B0%5D.openInputStream();%0A          _contentCached = StreamUtil.getContent( new InputStreamReader
 ( 
-l
 in
-e ).append( %22%5Cn%22 );%0A          %7D%0A          _contentCached = sb.toString();%0A            //new Scanner( files%5B0%5D.toJavaFile() ).useDelimiter( %22%5C%5CZ%22 ).next
+putStream ) );%0A          inputStream.close
 ();%0A

fix PropertiesType to use the IFile directly so as to use up-to-date info from IJ e.g., navigation works now without having to save changes