/* Copyright (c) 2006 Dirk Jagdmann <doj@cubic.org>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

    1. The origin of this software must not be misrepresented; you
       must not claim that you wrote the original software. If you use
       this software in a product, an acknowledgment in the product
       documentation would be appreciated but is not required.

    2. Altered source versions must be plainly marked as such, and
       must not be misrepresented as being the original software.

    3. This notice may not be removed or altered from any source
       distribution. */

/* $Header: /home/doj/code/cbmfs/cbmdisk.hpp,v 1.5 2006-07-21 14:46:04 doj Exp $ */

#pragma once
#include "cbmfs.hpp"

class CBMdisk
{
public:

#pragma pack(push, 1)

  struct TS {
    BYTE track;  //< track number,  valid range [1..]
    BYTE sector; //< sector number, valid range [0..255]
    TS(BYTE t, BYTE s) : track(t), sector(s) { }
    bool operator == (const TS& r) const { return track==r.track && sector==r.sector; }
    void clear() { track=sector=0; }
    void operator() (const BYTE t, const BYTE s) { track = t; sector = s; }
  };
  static_assert(sizeof(TS) == 2, "wrong packed size");

  struct direntry {
    TS next;
    BYTE FileType;
    TS file;
    BYTE filename[16];
    TS REL;
    union {
      BYTE RELlength;
      BYTE geos_file_structure;
    };
    BYTE geos_filetype;
    BYTE geos_year;
    BYTE geos_month;
    BYTE geos_day;
    BYTE geos_hour;
    BYTE geos_minute;
    BYTE totalSectorsLo;
    BYTE totalSectorsHi;
  };
  static_assert(sizeof(direntry) == 32, "wrong packed size");

  struct dirsector {
    direntry entry[8];
  };
  static_assert(sizeof(dirsector) == SECTOR_SIZE, "wrong packed size");

  struct bamentry {
    BYTE freeblocks;
    BYTE bm0, bm1, bm2;
  };
  static_assert(sizeof(bamentry) == 4, "wrong packed size");

  struct filesector {
    TS next;
    BYTE data[SECTOR_DATA_SIZE];
  };
  static_assert(sizeof(filesector) == SECTOR_SIZE, "wrong packed size");

#pragma pack(pop)

  CBMdisk();
  virtual ~CBMdisk();

  /// initialize a disk with the data @p disk_.
  /// @return 0 upon success.
  /// @return -1 upon failure.
  int init(BYTE *disk_);

  virtual TS dir() = 0;
  filesector* getsector(TS);
  filesector* newsector();
  filesector* newsector(const TS& prev_sec);
  const int fileLength(const TS&);
  direntry* get_direntry(const char *filename);
  direntry* new_direntry();
  int filenum();
  int freeblocks();
  virtual int maxblocks() = 0;
  virtual void bam_set(const BYTE t, const BYTE s, const bool b) = 0;
  virtual void printbam() = 0;
  void printdir();
  void printsectors();
  virtual int track_max() = 0;

  filesector* getsector(const BYTE t, const BYTE s) { return getsector(TS(t,s)); }
  dirsector* getdirsector(const TS& ts) { return reinterpret_cast<dirsector*>(getsector(ts)); }
  void bam_set(const TS& ts, const bool b) { this->bam_set(ts.track, ts.sector, b); }

  /// @return pointer to the 2 byte disk ID.
  /// @return NULL if the disk does not have an ID.
  virtual const BYTE* diskId() const = 0;

  /// @return pointer to the 16 byte disk name.
  /// @return NULL if th disk does not have a name.
  virtual const BYTE* diskName() const = 0;

  /// format the disk image.
  /// @return 0 upon success.
  /// @return -1 upon error.
  virtual int format() = 0;

  /// the directory sector interleave.
  int dirInterleave;
  /// th file sector interleave.
  int fileInterleave;

protected:
  virtual const int* sectorsPerTrack() = 0;
  virtual bool bam_get(const BYTE t, const BYTE s) = 0;
  bool bam_get(const TS& ts) { return this->bam_get(ts.track, ts.sector); }
  virtual void fix(bamentry*, const BYTE t) = 0;
  virtual bamentry* getbamentry(const BYTE t) = 0;

  /// @return true if track @p t should not be used for data. Typically the directory tracks are reserved.
  virtual bool reserved_track(const BYTE t) = 0;

  void printbamblocks();

  /// @return default directory sector interleave.
  virtual int dir_interleave() const = 0;
  /// @return default file sector interleave.
  virtual int file_interleave() const = 0;

  BYTE *disk;

private:
  filesector* try_allocate(const TS& ts);
};

extern CBMdisk *cbmdisk;
