// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename       : Read.cs
// Author(s)      : Natalia Portillo <claunia@claunia.com>
//
// Component      : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
//     Reads CisCopy disk images.
//
// --[ License ] --------------------------------------------------------------
//
//     This library is free software; you can redistribute it and/or modify
//     it under the terms of the GNU Lesser General Public License as
//     published by the Free Software Foundation; either version 2.1 of the
//     License, or (at your option) any later version.
//
//     This library is distributed in the hope that it will be useful, but
//     WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//     Lesser General Public License for more details.
//
//     You should have received a copy of the GNU Lesser General Public
//     License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/

using System;
using System.IO;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
using Aaru.Logging;

namespace Aaru.Images;

public sealed partial class CisCopy
{
#region IWritableImage Members

    /// <inheritdoc />
    public ErrorNumber Open(IFilter imageFilter)
    {
        Stream stream = imageFilter.GetDataForkStream();
        stream.Seek(0, SeekOrigin.Begin);

        var  type = (DiskType)stream.ReadByte();
        byte tracks;

        switch(type)
        {
            case DiskType.MD1DD8:
            case DiskType.MD1DD:
            case DiskType.MD2DD8:
            case DiskType.MD2DD:
                tracks = 80;

                break;
            case DiskType.MF2DD:
            case DiskType.MD2HD:
            case DiskType.MF2HD:
                tracks = 160;

                break;
            default:
                AaruLogging.Error(string.Format(Localization.Incorrect_disk_type_0, (byte)type));

                return ErrorNumber.InvalidArgument;
        }

        var trackBytes = new byte[tracks];
        stream.EnsureRead(trackBytes, 0, tracks);

        var cmpr = (Compression)stream.ReadByte();

        if(cmpr != Compression.None)
        {
            AaruLogging.Error(Localization.Compressed_images_are_not_supported);

            return ErrorNumber.NotImplemented;
        }

        int trackSize = type switch
                        {
                            DiskType.MD1DD8 or DiskType.MD2DD8                 => 8  * 512,
                            DiskType.MD1DD or DiskType.MD2DD or DiskType.MF2DD => 9  * 512,
                            DiskType.MD2HD                                     => 15 * 512,
                            DiskType.MF2HD                                     => 18 * 512
                        };

        var headStep = 1;

        if(type is DiskType.MD1DD or DiskType.MD1DD8) headStep = 2;

        var decodedImage = new MemoryStream();

        for(var i = 0; i < tracks; i += headStep)
        {
            var track = new byte[trackSize];

            if((TrackType)trackBytes[i] == TrackType.Copied)
                stream.EnsureRead(track, 0, trackSize);
            else
                ArrayHelpers.ArrayFill(track, (byte)0xF6);

            decodedImage.Write(track, 0, trackSize);
        }

        _imageInfo.Application          = "CisCopy";
        _imageInfo.CreationTime         = imageFilter.CreationTime;
        _imageInfo.LastModificationTime = imageFilter.LastWriteTime;
        _imageInfo.MediaTitle           = imageFilter.Filename;
        _imageInfo.ImageSize            = (ulong)(stream.Length - 2 - trackBytes.Length);
        _imageInfo.SectorSize           = 512;

        switch(type)
        {
            case DiskType.MD1DD8:
                _imageInfo.MediaType       = MediaType.DOS_525_SS_DD_8;
                _imageInfo.Sectors         = 40 * 1 * 8;
                _imageInfo.Heads           = 1;
                _imageInfo.Cylinders       = 40;
                _imageInfo.SectorsPerTrack = 8;

                break;
            case DiskType.MD2DD8:
                _imageInfo.MediaType       = MediaType.DOS_525_DS_DD_8;
                _imageInfo.Sectors         = 40 * 2 * 8;
                _imageInfo.Heads           = 2;
                _imageInfo.Cylinders       = 40;
                _imageInfo.SectorsPerTrack = 8;

                break;
            case DiskType.MD1DD:
                _imageInfo.MediaType       = MediaType.DOS_525_SS_DD_9;
                _imageInfo.Sectors         = 40 * 1 * 9;
                _imageInfo.Heads           = 1;
                _imageInfo.Cylinders       = 40;
                _imageInfo.SectorsPerTrack = 9;

                break;
            case DiskType.MD2DD:
                _imageInfo.MediaType       = MediaType.DOS_525_DS_DD_9;
                _imageInfo.Sectors         = 40 * 2 * 9;
                _imageInfo.Heads           = 2;
                _imageInfo.Cylinders       = 40;
                _imageInfo.SectorsPerTrack = 9;

                break;
            case DiskType.MF2DD:
                _imageInfo.MediaType       = MediaType.DOS_35_DS_DD_9;
                _imageInfo.Sectors         = 80 * 2 * 9;
                _imageInfo.Heads           = 2;
                _imageInfo.Cylinders       = 80;
                _imageInfo.SectorsPerTrack = 9;

                break;
            case DiskType.MD2HD:
                _imageInfo.MediaType       = MediaType.DOS_525_HD;
                _imageInfo.Sectors         = 80 * 2 * 15;
                _imageInfo.Heads           = 2;
                _imageInfo.Cylinders       = 80;
                _imageInfo.SectorsPerTrack = 15;

                break;
            case DiskType.MF2HD:
                _imageInfo.MediaType       = MediaType.DOS_35_HD;
                _imageInfo.Sectors         = 80 * 2 * 18;
                _imageInfo.Heads           = 2;
                _imageInfo.Cylinders       = 80;
                _imageInfo.SectorsPerTrack = 18;

                break;
        }

        _imageInfo.MetadataMediaType = MetadataMediaType.BlockMedia;
        _decodedDisk                 = decodedImage.ToArray();

        decodedImage.Close();

        AaruLogging.Verbose(Localization.CisCopy_image_contains_a_disk_of_type_0, _imageInfo.MediaType);

        return ErrorNumber.NoError;
    }

    /// <inheritdoc />
    public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)
    {
        sectorStatus = SectorStatus.Dumped;

        return ReadSectors(sectorAddress, negative, 1, out buffer, out _);
    }

    /// <inheritdoc />
    public ErrorNumber ReadSectors(ulong              sectorAddress, bool negative, uint length, out byte[] buffer,
                                   out SectorStatus[] sectorStatus)
    {
        buffer       = null;
        sectorStatus = null;

        if(negative) return ErrorNumber.NotSupported;

        if(sectorAddress > _imageInfo.Sectors - 1) return ErrorNumber.OutOfRange;

        if(sectorAddress + length > _imageInfo.Sectors) return ErrorNumber.OutOfRange;

        buffer       = new byte[length * _imageInfo.SectorSize];
        sectorStatus = new SectorStatus[length];
        for(uint i = 0; i < length; i++) sectorStatus[i] = SectorStatus.Dumped;

        Array.Copy(_decodedDisk, (int)sectorAddress * _imageInfo.SectorSize, buffer, 0, length * _imageInfo.SectorSize);

        return ErrorNumber.NoError;
    }

#endregion
}