/*
	Реализация спецификаций 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.util.*;

public class FileOutputStream extends OutputStream
{
	private static final int OPEN_ERROR_NO_ERROR = 0;
	private static final int OPEN_ERROR_FILE_NAME_NOT_SPECIFIED = 1;
	private static final int OPEN_ERROR_FILE_ACCESS_DENIED = 2;


	private int handle;
	private int openError;
	private Object lock;
	private String fileName;
	private DataDescriptor descriptor;

	public FileOutputStream(String fileName)
	{
		this(fileName, false);
	}

	public FileOutputStream(String fileName, boolean appending)
	{
		int h;
		char[] fn;
		if(fileName == null || (h = fileName.length()) == 0)
		{
			this.handle = 0;
			this.openError = OPEN_ERROR_FILE_NAME_NOT_SPECIFIED;
			this.lock = new Object();
			this.fileName = fileName;
			this.descriptor = null;
			return;
		}
		fileName.getChars(0, h, fn = new char[h + 1], 0);
		if((h = (int) MalikSystem.syscall(Array.getFirstElementAddress(fn),
				appending ? 0 : 2, 0x0010)) == 0)
		{
			this.handle = 0;
			this.openError = OPEN_ERROR_FILE_ACCESS_DENIED;
			this.lock = new Object();
			this.fileName = fileName;
			this.descriptor = null;
			return;
		}
		this.handle = h;
		this.openError = OPEN_ERROR_NO_ERROR;
		this.lock = new Object();
		this.fileName = fileName;
		this.descriptor = new DataDescriptor();
	}

	public String toString()
	{
		return (handle != 0 ? "Открытый для записи файл " : "Закрытый файл ").concat(fileName);
	}

	public void checkOpenError()
			throws IOException
	{
		switch(openError)
		{
		default:
			if(handle != 0)
			{
				return;
			}
			throw new IOException("FileOutputStream: " +
					"файл " + fileName + " был закрыт.");
		case OPEN_ERROR_FILE_NAME_NOT_SPECIFIED:
			throw new IOException("FileOutputStream: " +
					"имя файла не задано.");
		case OPEN_ERROR_FILE_ACCESS_DENIED:
			throw new IOException("FileOutputStream: " +
					"файл " + fileName + " недоступен для записи.");
		}
	}

	public boolean hasOpenError()
	{
		return openError != OPEN_ERROR_NO_ERROR || handle == 0;
	}

	public final void close()
			throws IOException
	{
		int h;
		int error = 0;
		synchronized(lock)
		{
			label0:
			{
				if((h = handle) == 0)
				{
					error = 1;
					break label0;
				}
				MalikSystem.syscall((long) h, 0x0011);
				handle = 0;
			}
		}
		if(error == 1)
		{
			throw new IOException("FileOutputStream: " +
					"файл " + fileName + " был закрыт.");
		}
	}

	public final void flush()
			throws IOException
	{
	}

	public final void write(int src)
			throws IOException
	{
		synchronized(lock)
		{
			checkOpenError();
			descriptor.setDataInfo(MalikSystem.getLocalVariableAddress(src), 1);
			writeFile();
		}
	}

	public final void write(byte[] src)
			throws IOException
	{
		int length;
		if(src == null)
		{
			throw new NullPointerException("FileOutputStream.write: " +
					"параметр src равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			checkOpenError();
			if((length = src.length) > 0)
			{
				descriptor.setDataInfo(src, 0, length);
				writeFile();
			}
		}
	}

	public final void write(byte[] src, int offset, int length)
			throws IOException
	{
		int lim;
		int len;
		if(src == null)
		{
			throw new NullPointerException("FileOutputStream.write: " +
					"параметр src равен нулевой ссылке.");
		}
		if((lim = offset + length) > (len = src.length) ||
				lim < offset || offset > len || offset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("FileOutputStream.write: " +
					"индекс выходит из диапазона.");
		}
		synchronized(lock)
		{
			checkOpenError();
			if(length > 0)
			{
				descriptor.setDataInfo(src, offset, length);
				writeFile();
			}
		}
	}

	public final String getFileName()
	{
		return fileName;
	}

	protected void $finalize$()
	{
		int h;
		if((h = handle) != 0)
		{
			MalikSystem.syscall((long) h, 0x0011);
			handle = 0;
		}
	}

	private void writeFile()
			throws InterruptedIOException
	{
		int required;
		int writed;
		DataDescriptor d = descriptor;
		writed = (int) MalikSystem.syscall(handle, d.getDescriptorAddress(), 0x0013);
		required = d.getLength();
		if(writed < required)
		{
			throw new InterruptedIOException("FileOutputStream.write: " +
					"процесс записи в файл " + fileName +
					" был прерван. Записано байт: " + writed +
					" из " + required + ".", writed);
		}
	}
}
