// gamenite - A graph library for board games
// Copyright (C) 2022 Dane Johnson

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//! Module to allow reading and writing to zipped board files
//!
//! This modules utilizes [serde] to write and read the board as a JSON object to a compressed file.
//! It also allows an image to stored, using [image] to convert to a png and saving it alongside the
//! graph json object.
use crate::Board;

use std::io;
use std::io::{ Write, Read, Cursor };
use std::fs::File;
use std::path::Path;

use image::io::Reader as ImageReader;
use image::DynamicImage;

/// Writes the board and optionally an image to the file indicated by `path`
pub fn write_board_to_file(board: &Board, image: Option<&DynamicImage>, path: &Path) -> io::Result<()> {
    let file = File::create(path)?;
    let mut ar = zip::ZipWriter::new(file);
    let options = zip::write::FileOptions::default();

    ar.start_file("graph.json", options)?;
    ar.write_all(&serde_json::to_vec(board)?)?;
    if let Some(image) = image {
        ar.start_file("image.png", options)?;
        let data = encode_png(image);
        ar.write_all(&data)?;
        ar.flush()?;
    }
    ar.finish()?;
    Ok(())
}

/// Reads the board and possibly an image from the file indicated by `path`
pub fn read_board_from_file(path: &Path) -> io::Result<(Board, Option<DynamicImage>)> {
    let file = File::open(path)?;
    let mut ar = zip::ZipArchive::new(file)?;

    let json_file = ar.by_name("graph.json")?;
    let board = serde_json::from_reader(json_file)?;
    let image = ar.by_name("image.png").ok();
    if image.is_none() {
        return Ok((board, None))
    }
    let mut image_file = image.unwrap();
    let mut buf = Vec::new();
    image_file.read_to_end(&mut buf)?;
    let image = decode_png(&buf);
    Ok((board, Some(image)))
}

/// Converts a [DynamicImage] to the PNG format
pub fn encode_png(image: &DynamicImage) -> Vec<u8> {
    let mut cursor = Cursor::new(Vec::new());
    image.write_to(&mut cursor, image::ImageOutputFormat::Png).unwrap();
    cursor.into_inner()
}

/// Converts a PNG image to a [DynamicImage]
pub fn decode_png(buf: &[u8]) -> DynamicImage {
    let cursor = Cursor::new(buf);
    let mut reader = ImageReader::new(cursor);
    reader.set_format(image::ImageFormat::Png);
    reader.decode().unwrap()
}
