/**********************************************************************
*
* File scanner
*
* Copyright (c) 2021 Dario Deledda. All rights reserved.
* Use of this source code is governed by an MIT license
* that can be found in the LICENSE file.
*
* TODO:
**********************************************************************/
import os

// STBI supported format
//        STBI_NO_JPEG *
//        STBI_NO_PNG  *
//        STBI_NO_BMP  *
//        STBI_NO_PSD
//        STBI_NO_TGA  *
//        STBI_NO_GIF  *
//        STBI_NO_HDR  *
//        STBI_NO_PIC  *
//        STBI_NO_PNM  *

/******************************************************************************
*
* Struct and Enums
*
******************************************************************************/
enum Item_type {
	file = 0
	folder
	// archive format
	zip = 16
	archive_file
	// graphic format, MUST stay after the other types!!
	bmp = 32
	jpg
	png
	gif
	tga
	ppm
	pgm
	pic
	hdr
}

pub struct Item {
pub mut:
	path                 string
	name                 string
	size                 u64
	i_type               Item_type = .file
	container_index      int  // used if the item is in a container (.zip, .rar, etc)
	container_item_index int  // index in the container if the item is contained
	need_extract         bool // if true need to extraction from the container
	drawable             bool // if true the image can be showed
	n_item               int  //
	rotation             int  // number of rotation of PI/2
}

struct Item_list {
pub mut:
	lst        []Item
	path_sep   string
	item_index int = -1 // image currently shown
	n_item     int  // number of images scanned
	loaded     bool // flag that indicate that the list is ready to  be used
}

/******************************************************************************
*
* Utility functions
*
******************************************************************************/
[inline]
fn modulo(x int, n int) int {
	return (x % n + n) % n
}

[inline]
fn get_extension(x string) Item_type {
	// 4 char extension check
	if x.len > 4 {
		ext4 := x[x.len - 4..].to_lower()
		match ext4 {
			// containers
			'.zip' { return .zip }
			// graphic formats
			'.jpg' { return .jpg }
			'.png' { return .png }
			'.bmp' { return .bmp }
			'.gif' { return .gif }
			'.tga' { return .tga }
			'.ppm' { return .ppm }
			'.pgm' { return .pgm }
			'.pic' { return .pic }
			'.hdr' { return .hdr }
			else {}
		}
	}
	// 5 char extension check
	if x.len > 5 {
		ext5 := x[x.len - 5..].to_lower()
		if ext5 == '.jpeg' {
			{
				return .jpg
			}
		}
	}
	return .file
}

[inline]
fn is_image(x Item_type) bool {
	if int(x) >= int(Item_type.bmp) {
		return true
	}
	return false
}

[inline]
fn is_container(x Item_type) bool {
	if x in [.zip, .folder] {
		return true
	}
	return false
}

[inline]
fn (item_list Item_list) is_inside_a_container() bool {
	if item_list.lst.len <= 0 || item_list.n_item <= 0 {
		return false
	}
	return item_list.lst[item_list.item_index].need_extract
}

[inline]
fn (item_list Item_list) get_file_path() string {
	if item_list.lst.len <= 0 || item_list.n_item <= 0 {
		return ''
	}
	if item_list.lst[item_list.item_index].path.len > 0 {
		return '${item_list.lst[item_list.item_index].path}${item_list.path_sep}${item_list.lst[item_list.item_index].name}'
	}
	return item_list.lst[item_list.item_index].name
}

/******************************************************************************
*
* Scan functions
*
******************************************************************************/
fn (mut item_list Item_list) scan_folder(path string, in_index int) ! {
	println('Scanning [${path}]')
	mut folder_list := []string{}
	lst := os.ls(path)!

	// manage the single files
	for c, x in lst {
		pt := '${path}${item_list.path_sep}${x}'
		mut item := Item{
			path: path
			name: x
			container_index: in_index
			container_item_index: c
		}
		if os.is_dir(pt) {
			folder_list << x
		} else {
			ext := get_extension(x)
			if ext == .zip {
				item.i_type = .zip
				item_list.lst << item
				item_list.scan_zip(pt, item_list.lst.len - 1)!
				continue
			}
			if is_image(ext) == true {
				item_list.n_item += 1
				item.n_item = item_list.n_item
				item.i_type = ext
				item.drawable = true
				item_list.lst << item
				continue
			}
		}
	}

	// manage the folders
	for x in folder_list {
		pt := '${path}${item_list.path_sep}${x}'
		item := Item{
			path: path
			name: x
			i_type: .folder
		}
		item_list.lst << item
		item_list.scan_folder(pt, item_list.lst.len - 1)!
	}
	// println(item_list.lst.len)
	// println("==================================")
}

fn (item_list Item_list) print_list() {
	println('================================')
	for x in item_list.lst {
		if x.i_type == .folder {
			print('[]')
		}
		if x.i_type == .zip {
			print('[ZIP]')
		}
		println('${x.path} => ${x.container_index} ${x.container_item_index} ${x.name} ne:${x.need_extract}')
	}
	println('n_item: ${item_list.n_item} index: ${item_list.item_index}')
	println('================================')
}

fn (mut item_list Item_list) get_items_list(args []string) {
	item_list.loaded = false
	println('Args: ${args}')

	item_list.path_sep = $if windows { '\\' } $else { '/' }
	for x in args {
		// scan folder
		if os.is_dir(x) {
			mut item := Item{
				path: x
				name: x
				container_index: item_list.lst.len
				i_type: .folder
			}
			item_list.lst << item
			item_list.scan_folder(x, item_list.lst.len - 1) or {
				eprintln('ERROR: scanning folder [${x}]!')
				continue
			}
		} else {
			mut item := Item{
				path: ''
				name: x
				container_index: -1
			}
			ext := get_extension(x)
			// scan .zip
			if ext == .zip {
				item.i_type = .zip
				item_list.lst << item
				item_list.scan_zip(x, item_list.lst.len - 1) or {
					eprintln('ERROR: scanning zip [${x}]!')
					continue
				}
				continue
			}
			// single images
			if is_image(ext) == true {
				item_list.n_item += 1
				item.n_item = item_list.n_item
				item.i_type = ext
				item.drawable = true
				item_list.lst << item
				continue
			}
		}
	}
	// debug call for list all the loaded items
	// item_list.print_list()

	println('Items: ${item_list.n_item}')
	println('Scanning done.')

	item_list.get_next_item(1)
	item_list.loaded = true
}

/******************************************************************************
*
* Navigation functions
*
******************************************************************************/
fn (mut item_list Item_list) get_next_item(in_inc int) {
	// if empty exit
	if item_list.lst.len <= 0 || item_list.n_item <= 0 {
		return
	}

	inc := if in_inc > 0 { 1 } else { -1 }
	mut i := item_list.item_index + in_inc
	i = modulo(i, item_list.lst.len)
	start := i
	for {
		if item_list.lst[i].drawable == true {
			item_list.item_index = i
			break
		}
		i = i + inc
		i = modulo(i, item_list.lst.len)
		// if we are in a loop break it
		if i == start {
			break
		}
	}
	// println("Found: ${item_list.item_index}")
}

fn (mut item_list Item_list) go_to_next_container(in_inc int) {
	// if empty exit
	if item_list.lst.len <= 0 || item_list.n_item <= 0 {
		return
	}
	inc := if in_inc > 0 { 1 } else { -1 }
	mut i := item_list.item_index + in_inc
	i = modulo(i, item_list.lst.len)
	start := i
	for {
		// check if we found a folder
		if is_container(item_list.lst[i].i_type) == true
			&& i != item_list.lst[item_list.item_index].container_index {
			item_list.item_index = i
			item_list.get_next_item(1)
			break
		}
		// continue to search
		i = i + inc
		i = modulo(i, item_list.lst.len)
		// if we are in a loop break it
		if i == start {
			break
		}
	}
}

/******************************************************************************
*
* Other functions
*
******************************************************************************/
[inline]
fn (mut item_list Item_list) rotate(in_inc int) {
	item_list.lst[item_list.item_index].rotation += in_inc
	if item_list.lst[item_list.item_index].rotation >= 4 {
		item_list.lst[item_list.item_index].rotation = 0
	}
}