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

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

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

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

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

package malik.emulator.io.cloud;

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

public class FileIOStream extends IOStream
{
    private static final int NO_ERROR = 0;
    private static final int FILE_NOT_FOUND = 1;
    private static final int FILE_NAME_NOT_SPECIFIED = 3;

    private static HandleInputStream createInput(String fileName, DataDescriptor descriptor, int handle) {
        return new HandleInputStream(fileName, descriptor, handle) {
            public final long getFileSize() {
                long result;
                synchronized(descriptor)
                {
                    int h;
                    result = (h = handle) == 0 ? -1L : MalikSystem.syscall((long) h, 0x0015);
                }
                return result;
            }
        };
    }

    private static HandleOutputStream createOutput(String fileName, DataDescriptor descriptor, int handle) {
        return new HandleOutputStream(fileName, descriptor, handle) {
        };
    }

    private final int openError;
    private int handle;
    private final String fileName;
    private final HandleInputStream input;
    private final HandleOutputStream output;
    private final DataDescriptor descriptor;

    public FileIOStream(String fileName) {
        int h;
        int e;
        int len;
        DataDescriptor descriptor;
        if(fileName == null || (len = fileName.length()) <= 0)
        {
            h = 0;
            e = FILE_NAME_NOT_SPECIFIED;
        } else
        {
            char[] name;
            fileName.getChars(0, len, name = new char[len + 1], 0);
            if((h = (int) MalikSystem.syscall(Array.getFirstElementAddress(name), 3, 0x0010)) == 0)
            {
                e = FILE_NOT_FOUND;
            } else
            {
                e = NO_ERROR;
            }
        }
        if(h == 0)
        {
            this.openError = e;
            this.fileName = fileName;
            this.input = null;
            this.output = null;
            this.descriptor = null;
            return;
        }
        descriptor = new DataDescriptor();
        this.openError = NO_ERROR;
        this.handle = h;
        this.fileName = fileName;
        this.input = createInput(fileName, descriptor, h);
        this.output = createOutput(fileName, descriptor, h);
        this.descriptor = descriptor;
    }

    FileIOStream(String fileName, int handle) {
        DataDescriptor descriptor = new DataDescriptor();
        this.openError = NO_ERROR;
        this.handle = handle;
        this.fileName = fileName;
        this.input = createInput(fileName, descriptor, handle);
        this.output = createOutput(fileName, descriptor, handle);
        this.descriptor = descriptor;
    }

    public void close() throws IOException {
        int error;
        checkOpenError();
        error = 0;
        synchronized(descriptor)
        {
            int h;
            if((h = handle) != 0)
            {
                int bufferPosition;
                HandleInputStream in = input;
                HandleOutputStream out = output;
                if((bufferPosition = out.position) > 0)
                {
                    out.position = 0;
                    if(!out.writeFile(bufferPosition)) error = 1;
                }
                handle = 0;
                in.handle = 0;
                out.handle = 0;
                MalikSystem.syscall((long) h, 0x0011);
            }
        }
        if(error == 1)
        {
            throw new IOException("FileIOStream.close: ошибка произошла при записи файла.");
        }
    }

    public void resetInputStream() throws IOException {
        int error;
        checkOpenError();
        error = 0;
        synchronized(descriptor)
        {
            label0:
            {
                if(handle == 0)
                {
                    error = 1;
                    break label0;
                }
                input.resetPosition();
            }
        }
        if(error == 1)
        {
            throw new ClosedFileException("FileIOStream.resetInputStream: файловый поток закрыт.");
        }
    }

    public void resetOutputStream() throws IOException {
        int error;
        checkOpenError();
        error = 0;
        synchronized(descriptor)
        {
            label0:
            {
                int h;
                int bufferPosition;
                long delta;
                HandleOutputStream out;
                if((h = handle) == 0)
                {
                    error = 1;
                    break label0;
                }
                if((bufferPosition = (out = output).position) > 0)
                {
                    out.position = 0;
                    if(!out.writeFile(bufferPosition))
                    {
                        error = 2;
                        break label0;
                    }
                }
                delta = 0L;
                delta = -MalikSystem.syscall(h, MalikSystem.getLocalVariableAddress(delta), 0x0014);
                MalikSystem.syscall(h, MalikSystem.getLocalVariableAddress(delta), 0x0014);
            }
        }
        switch(error)
        {
        case 1:
            throw new ClosedFileException("FileIOStream.resetOutputStream: файловый поток закрыт.");
        case 2:
            throw new IOException("FileIOStream.resetOutputStream: ошибка произошла при записи файла.");
        }
    }

    public boolean truncateSupported() {
        return true;
    }

    public boolean positionSupported() {
        return true;
    }

    public long truncate() throws IOException {
        int error;
        long result;
        checkOpenError();
        error = 0;
        synchronized(descriptor)
        {
            label0:
            {
                int h;
                int bufferPosition;
                HandleOutputStream out;
                if((h = handle) == 0)
                {
                    error = 1;
                    result = 0L;
                    break label0;
                }
                if((bufferPosition = (out = output).position) > 0)
                {
                    out.position = 0;
                    if(!out.writeFile(bufferPosition))
                    {
                        error = 2;
                        result = 0L;
                        break label0;
                    }
                }
                result = MalikSystem.syscall((long) h, 0x0016);
            }
        }
        switch(error)
        {
        case 1:
            throw new ClosedFileException("FileIOStream.truncate: файловый поток закрыт.");
        case 2:
            throw new IOException("FileIOStream.truncate: ошибка произошла при записи файла.");
        }
        return result;
    }

    public long position(long delta) throws IOException {
        int error;
        long result;
        checkOpenError();
        error = 0;
        synchronized(descriptor)
        {
            label0:
            {
                int h;
                int bufferPosition;
                HandleOutputStream out;
                if((h = handle) == 0)
                {
                    error = 1;
                    result = 0L;
                    break label0;
                }
                if((bufferPosition = (out = output).position) > 0)
                {
                    out.position = 0;
                    if(!out.writeFile(bufferPosition))
                    {
                        error = 2;
                        result = 0L;
                        break label0;
                    }
                }
                result = MalikSystem.syscall(h, MalikSystem.getLocalVariableAddress(delta), 0x0014);
            }
        }
        switch(error)
        {
        case 1:
            throw new ClosedFileException("FileIOStream.position: файловый поток закрыт.");
        case 2:
            throw new IOException("FileIOStream.position: ошибка произошла при записи файла.");
        }
        return result;
    }

    public InputStream getInputStream() throws IOException {
        checkOpenError();
        return input;
    }

    public OutputStream getOutputStream() throws IOException {
        checkOpenError();
        return output;
    }

    public void checkOpenError() throws IOException {
        String name = fileName;
        switch(openError)
        {
        case FILE_NOT_FOUND:
            throw new FileNotFoundException((new StringBuilder()).append("FileIOStream.checkOpenError: файл ").append(name).append(" не найден или занят.").toString(), name);
        case FILE_NAME_NOT_SPECIFIED:
            throw new IOException("FileIOStream.checkOpenError: имя файла не было задано при создании этого экземпляра CloudFileStream.");
        }
    }

    public boolean hasOpenError() {
        int e;
        return (e = openError) > NO_ERROR && e <= FILE_NAME_NOT_SPECIFIED;
    }

    public final long getFileSize() {
        HandleInputStream in;
        return (in = input) == null ? -1L : in.getFileSize();
    }

    public final String getFileName() {
        return fileName;
    }
}
