print all conflicts in the current channel
//! Like "pijul reset" but instead of overwriting the working copy, we
//! get a list of all the conflicts

use libpijul::pristine::{Inode, InodeMetadata};
use libpijul::working_copy::{WorkingCopy, WorkingCopyRead};
use libpijul::Conflict;
use libpijul::TxnT;

use anyhow::bail;

use std::io::Write;
use std::iter::IntoIterator;

use repository::Repository;
mod repository;

#[derive(Clone, Copy)]
struct FakeWorkingCopy;

impl WorkingCopyRead for FakeWorkingCopy {
    type Error = std::io::Error;

    fn file_metadata(&self, _file: &str) -> Result<InodeMetadata, Self::Error> {
        unimplemented!("file_metadata()")
    }

    fn read_file(&self, _file: &str, _buffer: &mut Vec<u8>) -> Result<(), Self::Error> {
        unimplemented!("file_read()")
    }

    fn modified_time(&self, _file: &str) -> Result<std::time::SystemTime, Self::Error> {
        unimplemented!("modified_time")
    }
}

impl WorkingCopy for FakeWorkingCopy {
    fn create_dir_all(&self, _path: &str) -> Result<(), Self::Error> {
        Ok(())
    }
    fn remove_path(&self, _name: &str, _rec: bool) -> Result<(), Self::Error> {
        Ok(())
    }
    fn rename(&self, _former: &str, _new: &str) -> Result<(), Self::Error> {
        Ok(())
    }
    fn set_permissions(&self, _name: &str, _permissions: u16) -> Result<(), Self::Error> {
        Ok(())
    }

    type Writer = std::io::Sink;
    fn write_file(&self, _file: &str, _inode: Inode) -> Result<Self::Writer, Self::Error> {
        Ok(std::io::sink())
    }
}

fn print_conflicts<I>(conflicts: I)
where
    I: IntoIterator<Item = Conflict>,
{
    println!("");
    println!("Conflicts");
    for c in conflicts {
        match c {
            Conflict::Name { path, .. } => println!("  name {}", path),
            Conflict::Order { path, line, .. } => println!("  order {}:{}", path, line),
            Conflict::Zombie { path, line, .. } => println!("  zombie {}:{}", path, line),
            Conflict::ZombieFile { path, .. } => println!("  zombiefile {}", path),
            Conflict::Cyclic { path, line, .. } => println!("  cycle {}:{}", path, line),
            Conflict::MultipleNames { path, names, .. } => {
                println!("  multiname {} {:?}", path, names)
            }
        }
    }
}

fn run() -> Result<(), anyhow::Error> {
    let repo = Repository::find_root(None)?;
    let txn = repo.pristine.arc_txn_begin()?;

    let cur_channel = txn.read().current_channel().unwrap_or("main").to_string();
    println!("On channel {}", cur_channel);

    let channel = if let Some(channel) = txn.read().load_channel(&cur_channel)? {
        channel
    } else {
        bail!("No such channel: {}", cur_channel);
    };
    let conflicts = libpijul::output::output_repository_no_pending(
        &FakeWorkingCopy,
        &repo.changes,
        &txn,
        &channel,
        "",   // prefix?
        true, //
        None,
        num_cpus::get(),
        0,
    )?;
    if !conflicts.is_empty() {
        print_conflicts(conflicts.into_iter());
    }
    Ok(())
}

fn main() {
    std::process::exit(if let Err(e) = run() {
        writeln!(std::io::stderr(), "fatal: {}", e).unwrap_or(());
        1
    } else {
        0
    });
}