﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace wintex4
{
	class FileSystem
	{
		private const string GET_DRIVES = "gdrv";
		private const string GET_DRIVES_BACK = "gdrb";
		private const string GET_DIR_CONTENTS = "gdir";
		private const string GET_DIR_CONTENTS_BACK = "gdib";
		private const string GET_FILE_ATTRIBUTES = "gfia";
		private const string GET_FILE_ATTRIBUTES_BACK = "gfab";
		private const string DELETE_FILE = "dfle";
		private const string DELETE_DIR = "ddir";
		private const string SEND_FILE_TO_SERVER_REQ = "ftsr";
		private const string SEND_FILE_TO_SERVER_ANS_START = "ftss";
		private const string SEND_FILE_TO_SERVER_ANS_DATA = "ftsd";
		private const string SEND_FILE_TO_SERVER_ANS_END = "ftse";
		private const string SEND_FILE_TO_CLIENT_START = "ftcs";
		private const string SEND_FILE_TO_CLIENT_DATA = "ftcd";
		private const string SEND_FILE_TO_CLIENT_END = "ftce";
		private const string DOES_REMOTE_FILE_EXIST = "drfe";
		private const string DOES_REMOTE_FILE_EXIST_ANS = "drfa";
		private const string REPORT_ERROR = "rerr";

		Wintex wintex;

		string receivingFilePath = "";

		public FileSystem(Wintex wt)
		{
			wintex = wt;
		}

		public void ReadData(string cmd, byte[] data)
		{
			try
			{
				switch (cmd)
				{
					case GET_DRIVES:
						GetDrives();
						break;
					case GET_DIR_CONTENTS:
						GetDirContents(System.Text.Encoding.Default.GetString(data));
						break;
					case GET_FILE_ATTRIBUTES:
						GetFileAttributes(System.Text.Encoding.Default.GetString(data));
						break;
					case DELETE_FILE:
						DeleteFile(System.Text.Encoding.Default.GetString(data));
						break;
					case DELETE_DIR:
						DeleteDir(System.Text.Encoding.Default.GetString(data));
						break;
					case SEND_FILE_TO_CLIENT_START:
						if (receivingFilePath != "") // shouldn't happen - just in case
						{
							receivingFilePath = ""; // prevent writing to the wrong file
						}
						else
						{
							receivingFilePath = System.Text.Encoding.Default.GetString(data);
						}
						break;
					case SEND_FILE_TO_CLIENT_DATA:
						ReceiveFile(data);
						break;
					case SEND_FILE_TO_CLIENT_END:
						receivingFilePath = "";
						break;
					case SEND_FILE_TO_SERVER_REQ:
						string[] paths = System.Text.Encoding.Default.GetString(data).Split('|');
						ReadAndSendFile(paths[0], paths[1]);
						break;
					case DOES_REMOTE_FILE_EXIST:
						string path = System.Text.Encoding.Default.GetString(data);
						string ans = System.IO.File.Exists(path).ToString();
						SendData(DOES_REMOTE_FILE_EXIST_ANS, ans);
						break;
				}
			}
			catch
			{
				Environment.Exit(Constants.EXIT_FILESYS_READ_DATA);
			}
		}

		private void SendData(string cmd)
		{
			wintex.SendData(Constants.MODULE_FILESYSTEM, cmd);
		}

		private void SendData(string cmd, string data)
		{
			wintex.SendData(Constants.MODULE_FILESYSTEM, cmd, data);
		}

		private void SendData(string cmd, byte[] data)
		{
			wintex.SendData(Constants.MODULE_FILESYSTEM, cmd, data);
		}

		private void GetDrives()
		{
			try
			{
				string drives = String.Join("|", Environment.GetLogicalDrives());
				SendData(GET_DRIVES_BACK, drives);
			}
			catch
			{
				Environment.Exit(Constants.EXIT_FILESYS_GET_DRIVES);
			}
		}

		private void GetDirContents(string path)
		{
			// sends this structure:
			// "currPath*.|upPath*..|folder\example*example\|file\example.sth*example.sth"

			try
			{
				string output = path + "*.|" + GetParentDir(path) + "*..";

				// add folders
				foreach (string dir in System.IO.Directory.GetDirectories(path))
				{
					string name = dir.Substring(dir.LastIndexOf('\\') + 1);
					output += "|" + dir + "*" + name + "\\";
				}

				// add files
				foreach (string file in System.IO.Directory.GetFiles(path))
				{
					string name = file.Substring(file.LastIndexOf('\\') + 1);
					output += "|" + file + "*" + name;
				}
				
				SendData(GET_DIR_CONTENTS_BACK, output);
			}
			catch (Exception e)
			{
				SendData(REPORT_ERROR, e.ToString());
			}
		}

		private void GetFileAttributes(string path)
		{
			try
			{
				System.IO.FileInfo fInfo = new System.IO.FileInfo(path);
				string times = fInfo.CreationTime.ToString() + "|"
					+ fInfo.LastWriteTime.ToString() + "|"
					+ fInfo.LastAccessTime.ToString();
				SendData(GET_FILE_ATTRIBUTES_BACK, times);
			}
			catch (Exception e)
			{
				SendData(REPORT_ERROR, e.ToString());
			}
		}

		private void DeleteFile(string path)
		{
			try
			{
				System.IO.File.Delete(path);
				GetDirContents(GetParentDir(path));
			}
			catch (Exception e)
			{
				SendData(REPORT_ERROR, e.ToString());
			}
		}

		private void DeleteDir(string path)
		{
			try
			{
				System.IO.Directory.Delete(path, true);
				GetDirContents(GetParentDir(path));
			}
			catch (Exception e)
			{
				SendData(REPORT_ERROR, e.ToString());
			}
		}

		private void ReadAndSendFile(string clientPath, string serverPath)
		{
			byte[] file = null;
			try
			{
				file = System.IO.File.ReadAllBytes(clientPath);
			}
			catch (Exception ex)
			{
				SendData(REPORT_ERROR, ex.ToString());
			}

			if (file != null)
			{
				SendData(SEND_FILE_TO_SERVER_ANS_START, serverPath);
				SendData(SEND_FILE_TO_SERVER_ANS_DATA, file);
				SendData(SEND_FILE_TO_SERVER_ANS_END);
			}
		}

		private void ReceiveFile(byte[] data)
		{
			try
			{
				System.IO.File.WriteAllBytes(receivingFilePath, data);
			}
			catch (Exception e)
			{
				SendData(REPORT_ERROR, e.ToString());
			}
		}

		private string GetParentDir(string dir)
		{
			try
			{
				if (dir.EndsWith(":\\") || dir.Equals("/")) return "/";
				dir = dir.TrimEnd('\\');
				return dir.Substring(0, (dir.LastIndexOf('\\') + 1));
			}
			catch
			{
				Environment.Exit(Constants.EXIT_FILESYS_GET_PARENT_DIR);
				return "";
			}
		}
	}
}
