/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package malik.emulator.io.cloud;

import java.io.*;
import malik.emulator.io.vfs.*;
import malik.emulator.time.*;

public final class CloudFileSystem extends Object
		implements VirtualFileSystemReadOnly, VirtualFileSystemReadWrite
{
	private static final VirtualFileSystemReadWrite INSTANCE;

	static
	{
		INSTANCE = new CloudFileSystem();
	}

	public static VirtualFileSystemReadWrite getInstance()
	{
		return INSTANCE;
	}

	static long computeFields(long time)
	{
		return GregorianCalendar.computeFields(time, 0);
	}

	static long computeTime(long fields)
	{
		return GregorianCalendar.computeTime(fields, 0);
	}


	private CloudFileSystem()
	{
	}

	public String toString()
	{
		return "Файловая система текущего приложения";
	}

	public void readAttributes(String fileName, FileAttributes attr)
			throws FileNotExistsException
	{
		int len;
		char[] fn;
		AttributesDescriptor descriptor;
		if(fileName == null)
		{
			throw new NullPointerException("CloudFileSystem.readAttributes: " +
					"параметр fileName равен нулевой ссылке.");
		}
		if(attr == null)
		{
			throw new NullPointerException("CloudFileSystem.readAttributes: " +
					"параметр attr равен нулевой ссылке.");
		}
		fileName.getChars(0, len = fileName.length(), fn = new char[len + 1], 0);
		if(((int) MalikSystem.syscall(Array.getFirstElementAddress(fn),
				(descriptor = new AttributesDescriptor()).getDescriptorAddress(), 0x001c)) == 0)
		{
			throw new FileNotExistsException("CloudFileSystem.readAttributes: " +
					"файл " + fileName + " не существует.");
		}
		attr.setAttributes(
				computeTime(descriptor.creationTime), computeTime(descriptor.lastAccessTime),
				computeTime(descriptor.lastWriteTime), descriptor.attributes);
	}

	public int getFileNameMaxLength()
	{
		return (int) MalikSystem.syscall(0L, 0x0016);
	}

	public FileEnumerator findFirst()
	{
		char[] fileName;
		char[] directory;
		FileInfoDescriptor descriptor;
		directory = new char[] {
				'/', 0
		};
		(descriptor = new FileInfoDescriptor()).length =
				(fileName = new char[getFileNameMaxLength() + 1]).length;
		descriptor.chars = Array.getFirstElementAddress(fileName);
		return new CloudFileEnumerator((int) MalikSystem.syscall(
				Array.getFirstElementAddress(directory),
				descriptor.getDescriptorAddress(), 0x0018), fileName, descriptor);
	}

	public FileEnumerator findFirst(String dirName)
			throws DirectoryNotExistsException
	{
		int lim;
		int handle;
		char[] fileName;
		char[] directory;
		FileInfoDescriptor descriptor;
		if(dirName == null)
		{
			throw new NullPointerException("CloudFileSystem.findFirst: " +
					"параметр dirName равен нулевой ссылке.");
		}
		if((lim = dirName.length()) > 0)
		{
			if(dirName.charAt(0) == '/')
			{
				if(dirName.charAt(lim - 1) == '/')
				{
					dirName.getChars(0, lim, directory = new char[lim + 1], 0);
				} else
				{
					dirName.getChars(0, lim, directory = new char[lim + 2], 0);
					directory[lim] = '/';
				}
			} else
			{
				if(dirName.charAt(lim - 1) == '/')
				{
					dirName.getChars(0, lim, directory = new char[lim + 2], 1);
				} else
				{
					dirName.getChars(0, lim, directory = new char[lim + 3], 1);
					directory[lim + 1] = '/';
				}
				directory[0] = '/';
			}
		} else
		{
			directory = new char[] {
					'/', 0
			};
		}
		(descriptor = new FileInfoDescriptor()).length =
				(fileName = new char[getFileNameMaxLength() + 1]).length;
		descriptor.chars = Array.getFirstElementAddress(fileName);
		if((handle = (int) MalikSystem.syscall(Array.getFirstElementAddress(directory), 
				descriptor.getDescriptorAddress(), 0x0018)) == 0)
		{
			throw new DirectoryNotExistsException("CloudFileSystem.findFirst: " +
					"папка " + dirName + " не существует.");
		}
		return handle > 0 ? new CloudFileEnumerator(handle, fileName, descriptor) : null;
	}

	public InputStream openFileForRead(String fileName)
			throws IOException
	{
		FileInputStream result;
		(result = new FileInputStream(fileName)).checkOpenError();
		return result;
	}

	public void writeAttributes(String fileName, FileAttributes attr)
			throws FileNotExistsException
	{
		int len;
		char[] fn;
		AttributesDescriptor descriptor;
		if(fileName == null)
		{
			throw new NullPointerException("CloudFileSystem.writeAttributes: " +
					"параметр fileName равен нулевой ссылке.");
		}
		if(attr == null)
		{
			throw new NullPointerException("CloudFileSystem.writeAttributes: " +
					"параметр attr равен нулевой ссылке.");
		}
		fileName.getChars(0, len = fileName.length(), fn = new char[len + 1], 0);
		(descriptor = new AttributesDescriptor()).attributes = attr.getAttributes() &
				(~FileAttributes.ATTR_DIRECTORY);
		descriptor.creationTime = computeFields(attr.getCreationTime());
		descriptor.lastAccessTime = computeFields(attr.getLastAccessTime());
		descriptor.lastWriteTime = computeFields(attr.getLastWriteTime());
		if(((int) MalikSystem.syscall(Array.getFirstElementAddress(fn),
				descriptor.getDescriptorAddress(), 0x001d)) == 0)
		{
			throw new FileNotExistsException("CloudFileSystem.writeAttributes: " +
					"файл " + fileName + " занят или не существует.");
		}
	}

	public void move(String oldFileName, String newFileName)
			throws IOException
	{
		int len;
		char[] ofn;
		char[] nfn;
		if(oldFileName == null)
		{
			throw new NullPointerException("CloudFileSystem.move: " +
					"параметр oldFileName равен нулевой ссылке.");
		}
		if(newFileName == null)
		{
			throw new NullPointerException("CloudFileSystem.move: " +
					"параметр newFileName равен нулевой ссылке.");
		}
		oldFileName.getChars(0, len = oldFileName.length(), ofn = new char[len + 1], 0);
		newFileName.getChars(0, len = newFileName.length(), nfn = new char[len + 1], 0);
		if(((int) MalikSystem.syscall(Array.getFirstElementAddress(ofn),
				Array.getFirstElementAddress(nfn), 0x001b)) == 0)
		{
			throw new IOException("CloudFileSystem.move: " +
					"не удалось переместить файл " + oldFileName +
					" на новое место " + newFileName + ".");
		}
	}

	public void deleteDirectory(String dirName)
			throws IOException
	{
		int len;
		char[] dn;
		if(dirName == null)
		{
			throw new NullPointerException("CloudFileSystem.deleteDirectory: " +
					"параметр dirName равен нулевой ссылке.");
		}
		dirName.getChars(0, len = dirName.length(), dn = new char[len + 1], 0);
		if(((int) MalikSystem.syscall((long) Array.getFirstElementAddress(dn), 0x001f)) == 0)
		{
			throw new IOException("CloudFileSystem.deleteDirectory: " +
					"не удалось удалить папку " + dirName + ".");
		}
	}

	public void deleteFile(String fileName)
			throws IOException
	{
		int len;
		char[] fn;
		if(fileName == null)
		{
			throw new NullPointerException("CloudFileSystem.deleteFile: " +
					"параметр fileName равен нулевой ссылке.");
		}
		fileName.getChars(0, len = fileName.length(), fn = new char[len + 1], 0);
		if(((int) MalikSystem.syscall((long) Array.getFirstElementAddress(fn), 0x0017)) == 0)
		{
			throw new IOException("CloudFileSystem.deleteFile: " +
					"не удалось удалить файл " + fileName + ".");
		}
	}

	public void createDirectory(String dirName)
			throws IOException
	{
		int len;
		char[] dn;
		if(dirName == null)
		{
			throw new NullPointerException("CloudFileSystem.createDirectory: " +
					"параметр dirName равен нулевой ссылке.");
		}
		dirName.getChars(0, len = dirName.length(), dn = new char[len + 1], 0);
		if(((int) MalikSystem.syscall((long) Array.getFirstElementAddress(dn), 0x001e)) == 0)
		{
			throw new IOException("CloudFileSystem.createDirectory: " +
					"не удалось создать папку " + dirName + ".");
		}
	}

	public OutputStream createFile(String fileName)
			throws IOException
	{
		FileOutputStream result;
		(result = new FileOutputStream(fileName, false)).checkOpenError();
		return result;
	}

	public OutputStream openFileForAppending(String fileName)
			throws IOException
	{
		FileOutputStream result;
		(result = new FileOutputStream(fileName, true)).checkOpenError();
		return result;
	}
}
