// ListParserVMS.cs
//
//  Based on code from edtftp.net (see CREDITS)
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace bareFTP.Protocol.Ftp
{
	public class ListParserVMS
	{
		 private static readonly string DIR = ".DIR";
    
        /// <summary>Directory field</summary>
        private static readonly string HDR = "Directory";
    
         /// <summary>Total field</summary>
        private static readonly string TOTAL = "Total";
    
         /// <summary> Number of expected fields</summary>
        private const int DEFAULT_BLOCKSIZE = 512*1024;
        
        /// <summary> Number of expected fields</summary>
        private static readonly int MIN_EXPECTED_FIELD_COUNT = 4;

        private static  bool versionInName = false;

        //private static int blocksize = DEFAULT_BLOCKSIZE;
		
		public static List<RemoteFile> ParseList(List<string> list)
		{
			if(list == null || list.Count < 1)
				return new List<RemoteFile>();

			List<RemoteFile> files = new List<RemoteFile>();
			System.Globalization.CultureInfo ParsingCulture = System.Globalization.CultureInfo.InvariantCulture;
			
			int counter = -1;
			
			foreach(string s in list)
			{
				
				counter++;
				List<string> fields = Split(s);
				
				if(fields.Count <= 0)
                	continue;
				
				if(fields.Count == 1 && fields[0].Contains(";"))
				{
					List<string> tmp_fields = Split(list[counter+1]);
					if(tmp_fields.Count == 3)
					{
						if(list[counter+1].StartsWith("%%"))
							continue;
						fields = Split(s + (list[counter+1].Trim()));
					}
					else
						continue;
				}
				// skip line which lists Directory
            	if (fields.Count >= 2 && fields[0].Equals(HDR))
		            continue;
    	        	// skip line which lists Total
        	    if (fields.Count > 0 && fields[0].Equals(TOTAL))
            	   	continue;
            	if (fields.Count < MIN_EXPECTED_FIELD_COUNT)
                	continue;
				if(fields[0].Trim().StartsWith("%%"))
					continue;
				 // skip blank lines
        		

				string name = fields[0];
        
	            // make sure it is the name (ends with ';<INT>')
	            int semiPos = name.LastIndexOf(';');
	            // check for ;
	            if(semiPos <= 0) 
	            {
	                throw new FormatException("File version number not found in name '" + name + "'");
	            }
	
	            string nameNoVersion = name.Substring(0, semiPos);
	            
	            // check for version after ;
	            string afterSemi = fields[0].Substring(semiPos+1);
	            try
	            {
	                Int64.Parse(afterSemi);
	                // didn't throw exception yet, must be number
	                // we don't use it currently but we might in future
	            }
	            catch(FormatException) 
	            {
	                // don't worry about version number
	            }
	
	            // test is dir
	            bool isDir = false;
	            if (nameNoVersion.EndsWith(DIR)) 
	            {
	                isDir = true;
	                name = nameNoVersion.Substring(0, nameNoVersion.Length-DIR.Length);
	            }
	            
	            if (!versionInName && !isDir)
	            {
	                name = nameNoVersion;
	            }
	            
	            // 2nd field is size USED/ALLOCATED format, or perhaps just USED
	            int slashPos = fields[1].IndexOf('/');
	            string sizeUsed = fields[1];
	            if (slashPos > 0)
	                sizeUsed = fields[1].Substring(0, slashPos);
	            try
				{
					long size = Int64.Parse(sizeUsed) * 512;
		            
		            // 3 & 4 fields are date time
		            string lastModifiedStr = TweakDateString(fields);
		            DateTime lastModified = DateTime.Parse(lastModifiedStr.ToString(),ParsingCulture.DateTimeFormat);
		            
		            // 5th field is [group,owner]
		            string group = null;
		            string owner = null;
		            if (fields.Count >= 5)
		            {
		                if (fields[4][0] == '[' && fields[4][fields[4].Length-1] == ']') 
		                {
		                    int commaPos = fields[4].IndexOf(',');
		                    if (commaPos < 0)
		                    {
		                        owner = fields[4].Substring(1, fields[4].Length-2);
		                        group = "";
		                    }
		                    else 
		                    {
		                        group = fields[4].Substring(1, commaPos-1);
		                        owner = fields[4].Substring(commaPos+1, fields[4].Length-commaPos-2);
		                    }
		                }
		            }
		            
		            // 6th field is permissions e.g. (RWED,RWED,RE,)
		            string permissions = null;
		            if (fields.Count >= 6)
		            {
		                if (fields[5][0] == '(' && fields[5][fields[5].Length-1] == ')') 
		                {
		                    permissions = fields[5].Substring(1, fields[5].Length-2);
		                }
		            }
	
					RemoteFile file = new RemoteFile();
					file.Filename = name;
					file.Size = size;
					file.Owner = owner;
					file.Group = group;
					file.Permissions = permissions;
					file.IsDir = isDir;
					file.LastModified = lastModified;
					files.Add(file);
				}
				catch{}
				
			}

			return files;
		}


		private static string TweakDateString(List<string> fields) 
        {
            // convert the last 2 chars of month to lower case
            StringBuilder lastModifiedStr = new StringBuilder();
            bool monthFound = false;
            for (int i = 0; i < fields[2].Length; i++)
            {
                if (!Char.IsLetter(fields[2][i])) 
                {
                    lastModifiedStr.Append(fields[2][i]);
                }
                else
                {
                    if (!monthFound)
                    {
                        lastModifiedStr.Append(fields[2][i]);
                        monthFound = true;
                    }
                    else
                    {
                        lastModifiedStr.Append(Char.ToLower(fields[2][i]));
                    }
                }
            }  
            lastModifiedStr.Append(" ").Append(fields[3]);
            return lastModifiedStr.ToString();
        }

		public static List<string> Split(string str)
        {
            ArrayList allTokens = new ArrayList(str.Split(null));
            for (int i = allTokens.Count - 1; i >= 0; i--)
                if (((string)allTokens[i]).Trim().Length == 0)
                    allTokens.RemoveAt(i);
            string[] ss = (string[])allTokens.ToArray(typeof(string));

			return new List<string>(ss);
        }
	}
}
