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

namespace Rokojori
{  
  public enum FilePathType
  {
    Absolute,
    Relative
  }

  public class FilePath
  {
    public FilePathType type;
    public FilePath parent;
    public string path;

    public string _fileName;
    public string _fileExtension;
    string _fullPath = null;

    Trillean isDirectory = Trillean.Any;

    public bool Exists()
    {
      return File.Exists( fullPath );
    }

    public void MarkAsFile()
    {
      isDirectory = Trillean.False;
    }

    public void MarkAsDirectory()
    {
      isDirectory = Trillean.True;
    }

    /**<summary for="method get_fileName">Only fileName without fileExtension </summary>*/
    public string fileName
    {
      get 
      {
        if ( _fileName == null)
        { 
          _fileName = Path.GetFileNameWithoutExtension( path );
        }

        return _fileName;
      }
    }
    
    /**<summary for="method get_fullFileName">Combines fileName + fileExtension </summary>*/
    public string fullFileName => fileName + fileExtension;

    /** <summary for="method get_fileExtension">File extension including the dot, eg. ".svg", ".jpg", ".js" </summary> */
    public string fileExtension
    {
      get 
      {
        if ( _fileExtension == null )
        { 
          _fileExtension = Path.GetExtension( path );
        }

        return _fileExtension;
      }
    }

    public bool HasFileExtension( string extension, bool forceDot = true )
    {
      if ( fileExtension == null )
      {
        return false;
      }

      if ( forceDot && ! extension.StartsWith( "." ))
      {
        extension = "." + extension;
      }

      return fileExtension.ToLower() == extension.ToLower();
    }

    public string fullPath
    {
      get
      {
        if ( _fullPath == null )
        {
          if ( this.parent != null )
          {
            _fullPath = this.parent.fullPath + "/" + path;
          }
          else
          {
            _fullPath = path;
          }
          
        }

        return _fullPath;
      }
    }

    public T LoadJSON<T>() where T:new()
    {
      return (T) FilesSync.LoadJSON<T>( absolutePath );
    }

    public string LoadUTF8()
    {
      return FilesSync.LoadUTF8( absolutePath );
    }

    
    public static FilePath Create( string path, FilePathType type = FilePathType.Relative, FilePath parent = null  )
    {
      
      var rp = new FilePath();

      if ( path.EndsWith( "/" ) )
      {
        path = path.ReplaceEnd( "/" );
        rp.isDirectory = Trillean.True;
      }

      path = path.ReplaceStart( "/" ); 

      rp.type = type;
      rp.parent = parent;
      rp.path = path;

      

      return rp;
    } 

    public FilePath WithExtension( string extension, bool forceDot = true )
    {
      var rp = new FilePath();

      rp.type = type;
      rp.parent = parent;

      if ( forceDot && ! extension.StartsWith( "." ) )
      {
        extension = "." + extension;
      }

      rp.path = Path.ChangeExtension( path, extension );

      return rp;
    }

    public string absolutePath
    {
      get 
      {
        return fullPath;
      }
    }

    public string parentAbsolutePath
    {
      get 
      {
        return RegexUtility.ParentPath( fullPath );
      }
    }

    public FilePath ChangeFileName( string fileName )
    {
      var parentPath = parentAbsolutePath;     

      if ( ! parentPath.EndsWith( "/" ) )
      {
        parentPath += "/";
      }

      return Absolute( parentPath + fileName + fileExtension );
    }

    public FilePath AddToFileName( string fileNameAddition )
    {
      return ChangeFileName( fileName + fileNameAddition );
    }

    public static FilePath Absolute( string path  )
    {
      return FilePath.Create( path, FilePathType.Absolute );
    }

    public FilePath CreateAbsoluteParent()
    {
      var path = this.fullPath;

      return Absolute( RegexUtility.ParentPath( path ) );
    } 
  

    public FilePath MakeRelative( string path )
    {
      return FilePath.Create( path, FilePathType.Relative, this );
    }

    
    public FilePath MakeAbsolutePathRelative( string otherAbsolutePath )
    {
      otherAbsolutePath = FilePath.Normalize( otherAbsolutePath );
      var ownAbsolutePath = FilePath.Normalize( fullPath );

      if ( ! otherAbsolutePath.StartsWith( ownAbsolutePath ) )
      {
        // A/B/C/D.x => own
        // A/B/E/F.x => other

        // A/B => common
        
        // E/F => common relative
        // /.. => own to common
        // /../E/F => own to other

        var commonPath = ownAbsolutePath.ExtractCommonStart( otherAbsolutePath );
        var commonFilePath = FilePath.Absolute( commonPath );
        var commonRelativeFilePath = commonFilePath.MakeAbsolutePathRelative( otherAbsolutePath );

        
        var ownIsDir = Trillean.True == isDirectory || Trillean.False != isDirectory && fileExtension == "";

        var itPath = ownAbsolutePath;

        if ( ! ownIsDir )
        {
          itPath = parentAbsolutePath;
        }

        itPath = itPath.ReplaceStart( commonPath );
        var splits = RegexUtility.SplitPaths( itPath );

        var dirs = "";

        for ( int i = 0; i < splits.Count; i++ )
        {
          if ( i !=  0 )
          {
            dirs += "/" ;
          }
          
          dirs += "..";
        }

        if ( ! commonRelativeFilePath.path.StartsWith( "/" ) )
        {
          dirs += "/";
        }

        // RJLog.Log(
        //   "\nown:", ownAbsolutePath,
        //   "\nother:", otherAbsolutePath,
        //   "\ncommonPath:", commonPath,
        //   "\ncommonRelativeFilePath:", commonRelativeFilePath.fullPath,
        //   "\ncommonRelativeFilePath.path:", commonRelativeFilePath.path,
        //   "\ndirs", dirs, 
        //   "\nRelative:", dirs + commonRelativeFilePath.path
        // );

        

        return MakeRelative( dirs + commonRelativeFilePath.path );
      }

      // A/B/C 
      // A/B/C/D 
      var relativePath = otherAbsolutePath.Substring( ownAbsolutePath.Length );
      
      relativePath = relativePath.ReplaceStart( "/" );


      return MakeRelative( relativePath );
    }

    public FilePath MakeRelative( FilePath path )
    {
      return CreateRelativeFromAbsolutePathWithParent( path.absolutePath, this );
    }

    public static FilePath CreateRelativeFromAbsolutePathWithParent( string absolutePath, FilePath absoluteParent )
    {
      var rp = new FilePath();
      
      rp.type = FilePathType.Relative;
      rp.parent = absoluteParent;
      rp.path = Path.GetFileName( absolutePath );
      rp._fullPath = absolutePath;

      return rp;

    }

    public static string Normalize( string path, bool removeFirst = true, bool removeLast = true )
    {
      var protocollStart = path.IndexOf( "://" );
      var protocoll = "";

      if ( protocollStart != -1 )
      {
        protocoll = path.Substring( 0, protocollStart + 3 );
        path = path.Substring( protocollStart + 3 ); 
      }

      path = Regex.Replace( path, @"\\", "/" );
      path = Regex.Replace( path, @"/+", "/" );

      if ( removeFirst )
      {
        path = Regex.Replace( path, @"^/", "" );
      }
      
      if ( removeLast )
      {
        path = Regex.Replace( path, @"/$", "" );
      }
      
      return protocoll + path;
    }

    public static string Join( params string[] paths )
    {
      var sb = new StringBuilder();

      for ( int i = 0; i < paths.Length; i++ )
      {
        var path = paths[ i ];

        path = Normalize( path );

        if ( i != 0 )
        {
          sb.Append( "/" );
        }

        sb.Append( path );
      }

      return sb.ToString();
    }

  }
}