/*
 * Copyright (c) 2013, 2014, 2015, 2023 Jonas 'Sortie' Termansen.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * filesystem.h
 * Filesystem.
 */

#ifndef FILESYSTEM_H
#define FILESYSTEM_H

bool is_8_3(const char* name);
void encode_8_3(const char* decoded, char encoded[8 + 3]);
void decode_8_3(const char encoded[8 + 3], char decoded[8 + 1 + 3 + 1]);
void timespec_to_fat(const struct timespec* ts, uint16_t* date, uint16_t* time,
                     uint8_t* tenths);
// TODO: Unify into the above.
uint8_t timespec_to_fat_tenths(struct timespec* ts);
uint16_t timespec_to_fat_time(struct timespec* ts);
uint16_t timespec_to_fat_date(struct timespec* ts);

class Device;
class Inode;

static const size_t INODE_HASH_LENGTH = 1 << 16;

class Filesystem
{
public:
	Filesystem(Device* device, const char* mount_path);
	~Filesystem();

public:
	Block* bpb_block;
	struct fat_bpb* bpb;
	Device* device;
	const char* mount_path;
	mode_t mode_reg;
	mode_t mode_dir;
	uid_t uid;
	gid_t gid;
	uint32_t block_size;
	uint16_t bytes_per_sector;
	uint16_t root_dirent_count;
	uint32_t sectors_per_fat;
	uint32_t root_inode_id;
	uint32_t total_sectors;
	uint32_t fat_sector; // TODO: Rename to lba
	uint32_t root_sector; // TODO: Rename to lba
	uint32_t data_sector; // TODO: Rename to lba
	uint32_t cluster_count;
	uint32_t cluster_size;
	uint8_t fat_type;
	uint32_t eio_cluster;
	uint32_t eof_cluster;
	uint32_t free_search;
	Inode* mru_inode;
	Inode* lru_inode;
	Inode* dirty_inode;
	Inode* hash_inodes[INODE_HASH_LENGTH];
	bool dirty;

public:
	Inode* GetInode(uint32_t inode_id, Block* dirent_block = NULL,
	                struct fat_dirent* dirent = NULL);
	uint32_t AllocateCluster();
	void FreeCluster(uint32_t cluster);
	uint32_t ReadFAT(uint32_t cluster);	
	bool WriteFAT(uint32_t cluster, uint32_t value);
	uint32_t CalculateFreeCount();
	void BeginWrite();
	void FinishWrite();
	void Sync();

};

#endif
