/*  OpenTally: Open-source election vote counting
 *  Copyright © 2021 Lee Yingtong Li (RunasSudo)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use crate::election::Election;
use crate::numbers::Rational;
use crate::parser;
use crate::writer;

use clap::{AppSettings, Clap};

use std::fs::File;

/// Convert between different ballot data formats
#[derive(Clap)]
#[clap(setting=AppSettings::DeriveDisplayOrder)]
pub struct SubcmdOptions {
	/// Path to the input data file
	#[clap(help_heading=Some("INPUT"))]
	infile: String,
	
	/// Format of input file
	#[clap(help_heading=Some("INPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
	r#in: Option<String>,
	
	/// Path to the output data file
	#[clap(help_heading=Some("OUTPUT"))]
	outfile: String,
	
	/// Format of output file
	#[clap(help_heading=Some("OUTPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
	out: Option<String>,
	
	/// Number of seats
	#[clap(help_heading=Some("ELECTION SPECIFICATION"), long)]
	seats: Option<usize>,
}

/// Entrypoint for subcommand
pub fn main(mut cmd_opts: SubcmdOptions) -> Result<(), i32> {
	// Auto-detect input/output formats
	if cmd_opts.r#in == None {
		if cmd_opts.infile.ends_with(".bin") {
			cmd_opts.r#in = Some("bin".to_string());
		} else if cmd_opts.infile.ends_with(".blt") {
			cmd_opts.r#in = Some("blt".to_string());
		} else if cmd_opts.infile.ends_with(".csp") {
			cmd_opts.r#in = Some("csp".to_string());
		} else {
			println!("Error: --in not specified and format cannot be determined from input filename");
			return Err(1);
		}
	}
	if cmd_opts.out == None {
		if cmd_opts.outfile.ends_with(".bin") {
			cmd_opts.out = Some("bin".to_string());
		} else if cmd_opts.outfile.ends_with(".blt") {
			cmd_opts.out = Some("blt".to_string());
		} else if cmd_opts.outfile.ends_with(".csp") {
			cmd_opts.out = Some("csp".to_string());
		} else {
			println!("Error: --out not specified and format cannot be determined from output filename");
			return Err(1);
		}
	}
	
	// Read input file
	let mut election: Election<Rational>;
	
	match cmd_opts.r#in.as_deref().unwrap() {
		"bin" => {
			election = parser::bin::parse_path(cmd_opts.infile);
		}
		"blt" => {
			match parser::blt::parse_path(cmd_opts.infile) {
				Ok(e) => {
					election = e;
				}
				Err(err) => {
					println!("Syntax Error: {}", err);
					return Err(1);
				}
			}
		}
		"csp" => {
			let file = File::open(cmd_opts.infile).expect("IO Error");
			election = parser::csp::parse_reader(file);
		}
		_ => unreachable!()
	};
	
	match cmd_opts.seats {
		Some(seats) => {
			election.seats = seats;
		}
		None => {
			if election.seats == 0 {
				println!("Error: --seats must be specified with CSP input");
				return Err(1);
			}
		}
	}
	
	// Write output file
	let output = File::create(cmd_opts.outfile).expect("IO Error");
	
	match cmd_opts.out.as_deref().unwrap() {
		"bin" => {
			writer::bin::write(election, output);
		}
		"blt" => {
			writer::blt::write(election, output);
		}
		"csp" => {
			writer::csp::write(election, output);
		}
		_ => unreachable!()
	}
	
	return Ok(());
}
