using System;
using System.Text.Json;
using System.Text.Json.Serialization;
 
 
public class Libro {
    public enum Tipo { TapaBlanda, TapaDura, Bolsillo }
    public /*required*/ Autor Autor { get; init; }
    public /*required*/ string Titulo { get; init; }
    public /*required*/ int Pags { get; init; }
    public /*required*/ string Isbn { get; init; }
    public /*required*/ Tipo Formato { get; init; }
 
    public override string ToString()
    {
        return $"{Autor.Nombre}: {Titulo} (ISBN: {Isbn}, {Pags} pgs.)";
    }
}
 
 
public class Autor {
    public Autor()
    {
        this.libros = new List<Libro>();
    }
 
    public /*required*/ string Nombre { get; init; }
    public /*required*/ int AnnoNac { get; init; }
 
    public Autor Inserta(Libro libro)
    {
        this.libros.Add( libro );
        return this;
    }
    public IList<Libro> Libros {
        get => ( (List<Libro>) this.libros ).AsReadOnly();
        init {
            this.libros.Clear();
            foreach (var libro in value) {
                this.libros.Add( libro );
            }
        }
    }
 
    public override string ToString()
    {
        string infoAutor = $"{this.Nombre} ({this.AnnoNac})";
        return infoAutor + "\n" + string.Join( '\n', this.Libros);
    }
 
    private IList<Libro> libros;
}
 
 
public class Biblioteca {
    public Biblioteca()
    {
        this.autores = new List<Autor>();
    }
 
    public /*required*/ string Nombre { get; init; }
    public IList<Autor> Autores
    {
        get => ( (List<Autor>) this.autores ).AsReadOnly();
        init {
            this.autores.Clear();
            foreach (var autor in value) {
                this.autores.Add( autor );
            }
        }
    }
 
    public Biblioteca Inserta(Autor autor)
    {
        this.autores.Add( autor );
        return this;
    }
 
    public override string ToString()
    {
        return this.Nombre + "\n" + string.Join( '\n', this.Autores );
    }
 
    private IList<Autor> autores;
}
 
 
public class JsonBiblioteca {
    public /*required*/ Biblioteca Biblioteca { get; init; }
 
    public void Save(string nf)
    {
        using var f = new FileStream( nf, FileMode.Create );
        var opts = new JsonSerializerOptions {
            ReferenceHandler = ReferenceHandler.Preserve
        };
        JsonSerializer.Serialize( f, this.Biblioteca, opts );
    }
 
    public static Biblioteca? Load(string nf)
    {
        using var f = new FileStream( nf, FileMode.Open );
        var opts = new JsonSerializerOptions {
            ReferenceHandler = ReferenceHandler.Preserve
        };
 
        //opts.Converters.Add( new BibliotecaJsonConverter() );
        //opts.Converters.Add( new AutorJsonConverter() );
        return JsonSerializer.Deserialize<Biblioteca>( f, opts );
    }
}
 
 
public class BibliotecaJsonConverter: JsonConverter<Biblioteca> {
    public override Biblioteca? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Biblioteca? toret = null;
        string nombre = "";
        ICollection<Autor>? autores = new List<Autor>();
 
        reader.Read();
        if ( reader.TokenType == JsonTokenType.PropertyName
            && reader.ValueTextEquals( "Nombre" ) )
        {
            reader.Read();
            nombre = reader.GetString() ?? "";
            reader.Read();
            autores = JsonSerializer.Deserialize<ICollection<Autor>>(ref reader, options);
 
            toret = new Biblioteca { Nombre = nombre };
            foreach (var autor in autores ?? new List<Autor>())
            {
                toret.Inserta( autor );
            }
 
            reader.Read(); // endobject
            reader.Read();
        }
 
        return toret;
    }
 
    public override void Write(Utf8JsonWriter writer, Biblioteca value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}
 
 
public class AutorJsonConverter: JsonConverter<Autor> {
    public override Autor? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Autor? toret = null;
        string nombre = "";
        int annonac = 1451; // se inventa la imprenta
        ICollection<Libro>? libros = new List<Libro>();
 
        reader.Read();
        while ( reader.TokenType != JsonTokenType.EndObject ) {
            if ( reader.TokenType == JsonTokenType.PropertyName
              && reader.ValueTextEquals( "Nombre" ) )
            {
                reader.Read();
                nombre = reader.GetString() ?? "";
            }
            else
            if ( reader.TokenType == JsonTokenType.PropertyName
                 && reader.ValueTextEquals( "AnnoNac" ) )
            {
                reader.Read();
                annonac = reader.GetInt32();
            }
            else
            if ( reader.TokenType == JsonTokenType.PropertyName
                 && reader.ValueTextEquals( "Libros" ) )
            {
                libros = JsonSerializer.Deserialize<ICollection<Libro>>(ref reader, options);
 
                toret = new Autor {
                    Nombre = nombre,
                    AnnoNac = annonac };
 
                foreach (var libro in libros ?? new List<Libro>())
                {
                    toret.Inserta( libro );
                }
            }
 
            reader.Read();
        }
 
        return toret;
    }
 
    public override void Write(Utf8JsonWriter writer, Autor value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}
 
 
class Test {
	static void Main()
	{
		var biblio = new Biblioteca { Nombre = "Universitaria UVigo" };
		var julio = new Autor { Nombre = "Julio Verne", AnnoNac = 1828 };
		var alex = new Autor { Nombre = "Alejandro Dumas", AnnoNac = 1802 };
 
		biblio.Inserta( julio )
        		.Inserta( alex );
 
		var libro1 = new Libro {
		    Titulo = "Viaje al centro de la tierra",
		    Autor = julio,
		    Formato = Libro.Tipo.Bolsillo,
		    Isbn = "978-8468253572",
		    Pags = 330
		};
 
		var libro2 = new Libro {
		    Titulo = "Los tres mosqueteros",
		    Autor = alex,
		    Formato = Libro.Tipo.TapaBlanda,
		    Isbn = "978-8491052401",
		    Pags = 617
		};
 
		var libro3 = new Libro {
		    Titulo = "La vuelta al mundo en ochenta días",
		    Autor = julio,
		    Formato = Libro.Tipo.TapaDura,
		    Isbn = "978-8431662950",
		    Pags = 384
		};
 
		julio.Inserta( libro1 )
		    .Inserta( libro3 );
		alex.Inserta( libro2 );
 
		Console.WriteLine( biblio );
		new JsonBiblioteca{ Biblioteca = biblio }.Save( "biblio.json" );
 
		Console.WriteLine( "Lectura:" );
		var biblio2 = JsonBiblioteca.Load( "biblio.json" );
		Console.WriteLine( biblio2 );
	}
}