using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

/// One row of cases.csv.
public class CaseDefinition
{
    public int    Number        { get; set; }   // Number
    public string Type          { get; set; }   // Type          心音 / 呼吸音
    public string CategoryJp    { get; set; }   // Category
    public string SubcategoryJp { get; set; }   // Subcategory
    public string LocationJp    { get; set; }   // Location
    public string TreeLevel1    { get; set; }   // Tree_Level1
    public string TreeLevel2    { get; set; }   // Tree_Level2
    public string TreeLevel3    { get; set; }   // Tree_Level3
    public string MapFront      { get; set; }   // Image_File
    public string SoundPath     { get; set; }   // Sound_File
    public string MapRight      { get; set; }   // Image_Right
    public string MapLeft       { get; set; }   // Image_Left
    public string MapBack       { get; set; }   // Image_Back

    public bool IsHeart
    {
        get { return !string.IsNullOrEmpty(Type) && Type.Contains("心"); }
    }

    /// Non-empty levels only, e.g. 呼吸音 → 正常呼吸音 → 気管音.
    /// Depth varies per row, which is what drives the drill-down.
    public string[] TreePath
    {
        get
        {
            var parts = new List<string>();
            foreach (string s in new[] { Type, TreeLevel1, TreeLevel2, TreeLevel3 })
                if (!string.IsNullOrWhiteSpace(s)) parts.Add(s.Trim());
            return parts.ToArray();
        }
    }

    public override string ToString()
    {
        return Number + ": " + string.Join(" : ", TreePath);
    }

    // ── loader ─────────────────────────────────────────────────────────

    public static List<CaseDefinition> LoadCasesFromCsv(string path)
    {
        var list = new List<CaseDefinition>();

        if (!File.Exists(path))
        {
            Console.WriteLine("CSV not found: " + path);
            return list;
        }

        // Encoding.UTF8 with BOM detection. Reading Japanese as the default
        // ANSI codepage gives mojibake that only shows up on the buttons.
        string[] lines;
        try
        {
            lines = File.ReadAllLines(path, Encoding.UTF8);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Failed to read " + path + ": " + ex.Message);
            return list;
        }

        if (lines.Length < 2)
        {
            Console.WriteLine("CSV has no data rows: " + path);
            return list;
        }

        // Your file is TAB separated; fall back to comma if it gets re-exported.
        char sep = lines[0].Contains("\t") ? '\t' : ',';

        // Header name -> column index, so adding or reordering columns is safe.
        // string[] header = lines[0].TrimStart('\uFEFF').Split(sep);
        string[] header = lines[0].TrimStart(new[] { '\uFEFF' }).Split(sep);
        var idx = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        for (int i = 0; i < header.Length; i++)
        {
            string key = header[i].Trim();
            if (key.Length > 0 && !idx.ContainsKey(key)) idx[key] = i;
        }

        if (!idx.ContainsKey("Number"))
        {
            Console.WriteLine("CSV header has no 'Number' column — wrong file or wrong delimiter?");
            return list;
        }

        Func<string[], string, string> get = (fields, name) =>
        {
            int i;
            if (!idx.TryGetValue(name, out i) || i >= fields.Length) return "";
            return fields[i].Trim();
        };

        int skipped = 0;

        for (int r = 1; r < lines.Length; r++)
        {
            if (string.IsNullOrWhiteSpace(lines[r])) continue;

            string[] f = lines[r].Split(sep);

            int number;
            if (!int.TryParse(get(f, "Number"), NumberStyles.Integer,
                              CultureInfo.InvariantCulture, out number))
            {
                skipped++;
                Console.WriteLine("Line " + (r + 1) + ": bad Number, skipped");
                continue;
            }

            list.Add(new CaseDefinition
            {
                Number        = number,
                Type          = get(f, "Type"),
                CategoryJp    = get(f, "Category"),
                SubcategoryJp = get(f, "Subcategory"),
                LocationJp    = get(f, "Location"),
                TreeLevel1    = get(f, "Tree_Level1"),
                TreeLevel2    = get(f, "Tree_Level2"),
                TreeLevel3    = get(f, "Tree_Level3"),
                MapFront      = get(f, "Image_File"),
                SoundPath     = get(f, "Sound_File"),
                MapRight      = get(f, "Image_Right"),
                MapLeft       = get(f, "Image_Left"),
                MapBack       = get(f, "Image_Back")
            });
        }

        Console.WriteLine("Loaded " + list.Count + " cases from " + path +
                          (skipped > 0 ? " (" + skipped + " skipped)" : ""));
        return list;
    }
}