package DTA::Schema::Result::Book;

use strict;
use warnings;

use URN;
use XML::LibXSLT;
use JSON qw(from_json);
use List::MoreUtils qw(uniq first_index);
use Metadata::COinS;
use Cache::FileCache;
use File::Temp qw(tempfile);
use LWP::UserAgent;

use Moose;
use MooseX::NonMoose;
use namespace::autoclean;
extends 'DBIx::Class::Core';

__PACKAGE__->load_components("InflateColumn::DateTime");

=head1 NAME

DTA::Schema::Result::Book

Schema for book with merged metadata table

Major cleanup on 12.11.2019

=cut

__PACKAGE__->table("book");

__PACKAGE__->add_columns(
    "id_book",
    { data_type => "integer", is_nullable => 0 },

#  Digital Object Identifier, https://de.wikipedia.org/wiki/Digital_Object_Identifier
    "doi",
    {   data_type     => "varchar",
        default_value => \"NULL::character varying",
        is_nullable   => 1,
        size          => 100,
    },
    "title",
    { data_type => "text", is_nullable => 1 },
    "subtitle",
    { data_type => "text", is_nullable => 1 },
    "other_title",
    { data_type => "text", is_nullable => 1 },
    "short_title",
    { data_type => "text", is_nullable => 1 },
    "autor1_prename",
    { data_type => "text", is_nullable => 1 },
    "autor1_lastname",
    { data_type => "text", is_nullable => 1 },
    "autor1_spelling",
    { data_type => "text", is_nullable => 1 },
    "autor1_syn_names",
    { data_type => "text", is_nullable => 1 },
    "autor2_prename",
    { data_type => "text", is_nullable => 1 },
    "autor2_lastname",
    { data_type => "text", is_nullable => 1 },
    "autor2_spelling",
    { data_type => "text", is_nullable => 1 },
    "autor2_syn_names",
    { data_type => "text", is_nullable => 1 },
    "uebersetzer",
    { data_type => "text", is_nullable => 1 },
    "year",
    { data_type => "text", is_nullable => 1 },
    "publisher",
    { data_type => "text", is_nullable => 1 },
    "autor3_prename",
    { data_type => "text", is_nullable => 1 },
    "autor3_lastname",
    { data_type => "text", is_nullable => 1 },
    "autor3_spelling",
    { data_type => "text", is_nullable => 1 },
    "autor3_syn_names",
    { data_type => "text", is_nullable => 1 },
    "special_comment",
    { data_type => "text", is_nullable => 1 },
    "dta_auflage",
    { data_type => "text", is_nullable => 1 },
    "dta_edition",
    { data_type => "text", is_nullable => 1 },
    "dta_pub_date",
    { data_type => "text", is_nullable => 1 },
    "dta_pub_location",
    { data_type => "text", is_nullable => 1 },
    "dta_pub_verlag",
    { data_type => "text", is_nullable => 1 },
    "umfang",
    { data_type => "text", is_nullable => 1 },
    "format",
    { data_type => "text", is_nullable => 1 },
    "umfang_normiert",
    { data_type => "integer", is_nullable => 1 },
    "band_zaehlung",
    { data_type => "bigint", is_nullable => 1 },
    "band_anzahl",
    { data_type => "bigint", is_nullable => 1 },
    "band_alphanum",
    { data_type => "text", is_nullable => 1 },
    "reihentitel",
    { data_type => "text", is_nullable => 1 },
    "autor1_pnd",
    { data_type => "text", is_nullable => 1 },
    "autor2_pnd",
    { data_type => "text", is_nullable => 1 },
    "autor3_pnd",
    { data_type => "text", is_nullable => 1 },
    "dta_seiten",
    { data_type => "text", is_nullable => 1 },
    "dta_reihe_titel",
    { data_type => "text", is_nullable => 1 },
    "dta_reihe_band",
    { data_type => "text", is_nullable => 1 },
    "dta_reihe_jahrgang",
    { data_type => "text", is_nullable => 1 },
    "dta_comment2",
    { data_type => "text", is_nullable => 1 },
    "dta_bibl_angabe",
    { data_type => "text", is_nullable => 1 },
    "ready",
    { data_type => "smallint", is_nullable => 1 },

    # we merged the table metadaten from here on
    # ==========================================
    "genre",
    { data_type => "text", is_nullable => 1 },
    "type",
    { data_type => "text", is_nullable => 1 },
    "klassifikation",
    { data_type => "text", is_nullable => 1 },
    "untergenre",
    { data_type => "text", is_nullable => 1 },
    "dirname",
    { data_type => "text", is_nullable => 1 },
    "schriftart",
    { data_type => "varchar", is_nullable => 0, size => 45 },
    "startseite",
    { data_type => "integer", is_nullable => 0 },
    "dwds_kategorie1",
    { data_type => "text", is_nullable => 1 },
    "dwds_kategorie2",
    { data_type => "text", is_nullable => 1 },
    "dwds_unterkategorie1",
    { data_type => "text", is_nullable => 1 },
    "dwds_unterkategorie2",
    { data_type => "text", is_nullable => 1 },

# ==========================================
# neu:
# Aus Datei 'recent.yml' gewonnen, diese wurde früher geparst, pro Eintrag wurde dann eine
# DB-Abfrage gemacht, skaliert schlecht bei großem Buchbestand
    "date_ingest" => { data_type => "date", is_nullable => 1 },

    # Zusätzliche Rolle, siehe http://redmine.dwds.de/issues/179 .
    "curator" => { data_type => "integer", is_nullable => 1 },

	"relax_ng_schema" => {
		data_type     => 'relax_ng_schema',
		default_value => 'basisformat.rng',
		extra => { list => DTA->config->{relax_ng}->{schemata} },
		is_nullable   => 0,
		},
	);
__PACKAGE__->set_primary_key("id_book");

=head1 RELATIONS

=head2 book_collections

Type: has_many

Related object: L<DTA::Schema::Result::BookCollection>

=cut

__PACKAGE__->has_many(
    "book_collections",
    "DTA::Schema::Result::BookCollection",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);

=head2 event_books

Type: has_many

Related object: L<DTA::Schema::Result::EventBook>

=cut

__PACKAGE__->has_many(
    "event_books",
    "DTA::Schema::Result::EventBook",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);

=head2 formulas

Type: has_many

Related object: L<DTA::Schema::Result::Formula>

=cut

__PACKAGE__->has_many(
    "formulas",
    "DTA::Schema::Result::Formula",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);


=head2 metareviews

Type: has_many

Related object: L<DTA::Schema::Result::Metareview>

=cut

__PACKAGE__->has_many(
    "metareviews",
    "DTA::Schema::Result::Metareview",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);

=head2 pagenotes

Type: has_many

Related object: L<DTA::Schema::Result::Pagenote>

=cut

__PACKAGE__->has_many(
    "pagenotes",
    "DTA::Schema::Result::Pagenote",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);

=head2 source

Type: might_have

Related object: L<DTA::Schema::Result::Source>

=cut

__PACKAGE__->might_have(
    "source",
    "DTA::Schema::Result::Source",
    { "foreign.id_book" => "self.id_book" },
    { cascade_copy      => 0, cascade_delete => 0 },
);


__PACKAGE__->has_many( 'textpages' => 'DTA::Schema::Result::Page', 'book' );

__PACKAGE__->has_many(
    "book_collections",
    "DTA::Schema::Result::BookCollection",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);
__PACKAGE__->many_to_many( collections => 'book_collections', 'collection' );

__PACKAGE__->might_have(
    "bookplace",
    "DTA::Schema::Result::BookPlace",
    { "foreign.book" => "self.id_book" },
    { cascade_copy   => 0, cascade_delete => 0 },
);

__PACKAGE__->has_many(
    'opentasks' => 'DTA::Schema::Result::OpenTask',
    'id_book'
);

sub pages {
    my $self = shift;
    return glob sprintf "%s/%s.xml/pages/*.xml", DTA->config->{xml_pages},
        $self->dirname;
}

# sub open_tickets {
#     my $self = shift;
#     return [
#         $self->tickets(
#             {   'me.status' => { '!=' => 8 },
#                 'me.page'   => { '!=' => undef },
#             },
#             {   prefetch => [qw/ reporter book ticketcomments /],
#                 order_by => 'me.page, me.changed DESC, me.created DESC',
#             },
#         )->all
#     ];
# }

# sub open_tickets_wb {
#     my $self = shift;
#     return [
#         $self->tickets(
#             {   'me.status' => { '!=' => 8 },
#                 'me.page'   => undef
#             },
#             {   prefetch => [qw/ reporter book ticketcomments /],
#                 order_by => 'me.changed DESC, me.created DESC',
#             },
#         )->all
#     ];
# }

# sub closed_tickets {
#     my $self = shift;
#     return [
#         $self->tickets(
#             { 'me.status' => 8, },
#             {   prefetch => [qw/ reporter book ticketcomments /],
#                 order_by => 'page, changed DESC',
#             }
#         )->all
#     ];
# }

# sub locking_tickets {
#     my $self = shift;
#     return $self->tickets->search(
#         {
# # @deprecated (Buch gesperrt, "In Bearbeitung", um das XML ausserhalb von DTA zu bearbeiten)
# # in <DTA::Schema::Result::Book::Lock> lock_type: 'revision'
#             'me.type'   => 12,
#             'me.status' => { '!=' => 8 }
#         }
#     )->count();
# }

# sub locked {
#     my $self = shift;
#     return $self->lock_history->search(
#         {   'me.un_locked_by'   => undef,
#             'me.date_un_locked' => undef,
#         },
#         {   prefetch =>
#                 'locked_by_account',    # isa L<DTA::Schema::Result::Account>
#         }
#     )->first;
# }

sub fundstelle {
    my $self = shift;
    my $task
        = $self->opentasks->search( { task_type => 10 }, { cache => 1 } );
    return unless $task->first;
    my $fundstellen = DTA->model('DTADB::Fundstellen')->search(
        {   id_book        => $self->id_book,
            id_Fundstellen => $task->first->id_fundstelle
        },
        {   cache    => 1,
            prefetch => ['id_book_locations'],
        },
    );
    return unless $fundstellen->first;
    return $fundstellen->first;
}

sub bibref {
    my $self = shift;
    my $task
        = $self->opentasks->search( { task_type => 10 }, { cache => 1 } );
    return unless $task->first;
    my $fundstellen = DTA->model('DTADB::Fundstellen')->search(
        {   id_book        => $self->id_book,
            id_Fundstellen => $task->first->id_fundstelle
        },
        {   cache    => 1,
            prefetch => ['id_book_locations'],
        },
    );
    return unless $fundstellen->first;

    my $location = $fundstellen->first->get_column('id_book_locations');
    my $bib_id   = $fundstellen->first->get_column('bib_id');
    return unless $bib_id;

    my @fields = split /\s*;\s*/, $bib_id;
    my %res;
    foreach my $field (@fields) {
        if ( $field =~ /^ppn(?::\s*)?([0-9X]+)$/i ) {
            $res{ppn} = $1;
        }
        elsif ( $field =~ /^epn(?::\s*)?([0-9X]+)$/i ) {
            $res{epn} = $1;
        }
        elsif ( $field =~ /^urn:\s*(\S+)$/i ) {
            $res{urn} = $1;
        }
        elsif ( $field =~ /^id:\s*(\S+)$/i ) {
            $res{id} = $1;
        }
        elsif ( $field =~ /^oclc-nr\.:\s*(\S+)$/i ) {
            $res{oclc} = $1;
        }
        elsif ( $field =~ /^(https?:\/\/\S+)$/ ) {
            $res{url} = $1;
        }
        elsif ( $field =~ /^dig:\s*(https?:\/\/\S+)$/i ) {
            $res{url} = $1;
        }
    }

    my %map = (
        162 => {
            ppn =>
                'https://opac.sub.uni-goettingen.de/DB=1/SET=2/TTL=18/MAT=/NOMAT=T/CLK?IKT=12&TRM=%s'
        },
        164 => {
            ppn =>
                'http://stabikat.sbb.spk-berlin.de/DB=1/XMLPRS=N/PPN?PPN=%s',

            # 2016-02-24
            #ppn => 'http://stabikat.de/DB=1/PPN?PPN=%s',
            epn =>
                'http://stabikat.de/DB=1/SET=12/TTL=1/CMD?ACT=SRCHA&IKT=1016&SRT=YOP&TRM=%s',
        },
    );

    if ( exists $map{$location} ) {
        while ( my ( $k, $v ) = each %{ $map{$location} } ) {
            if ( exists( $res{$k} ) ) {
                return sprintf( $v, $res{$k} );
            }
        }
    }
    elsif ( exists $res{url} ) {
        return $res{url};
    }
    elsif ( exists $res{urn} ) {
        return sprintf( "http://nbn-resolving.de/%s", $res{urn} );
    }
    else {
        return;
    }
}

=head2 related

hardwired book relations in book_related table

have a priority over books related by author name

storing them in a textfile is deprecated for now

=cut

sub related {
    my $self = shift;

    my $rel = DTA->model('DTADB::BookRelated')->find(
        {},
        {   from => 'book_related me where ? = ANY(relation)',
            bind => [ $self->id_book ],
        }
    );

    my $related;
    if ( $rel && grep( $self->id, $rel->relation ) ) {
        my $order_by = join ', ' => map { sprintf 'me.id_book = %d DESC', $_ }
            $rel->relation;
        $related
            = DTA->model('DTADB::Book')
            ->search( { 'me.id_book' => { -in => $rel->relation }, },
            { order_by => $order_by, } );
    }
    else {
        $related = DTA->model('DTADB::Book')->search(
            {   'me.id_book'         => { '!=' => $self->id },
                'me.title'           => $self->title,
                'me.ready'           => { -in => [ 1, 2 ] },
                'me.autor1_prename'  => $self->autor1_prename,
                'me.autor1_lastname' => $self->autor1_lastname,
            },
            { order_by => 'band_zaehlung', }
        );
    }
    return $related;
}

=head2 semantic_book_relations

semantic related books

the calculation of the relation is done within DDC via cron

=cut

sub semantic_related_books {
    my ( $self, $n ) = @_;

    $n = 20 unless defined $n;
    my @semantic_related_books = ();

    my $ua = LWP::UserAgent->new;
    $ua->timeout(10);

# eg http://kaskade.dwds.de/dstar/dta/semcloud/semq.perl?to=books&k=50&b=2&beta=-1&q=book%3Dsanders_schliemann_1884
    my $res
        = $ua->get( DTA->config->{dta}->{semcloud}->{url}
            . '?to=books&k=50&b=2&beta=-1&q=book%3D'
            . $self->dirname );
    return \@semantic_related_books unless $res->is_success;

    # we get json data, 'sim' holds the magnitude of the semantic relation
    my $json             = from_json( $res->content );
    my @dirnames_related = grep { $_ ne $self->dirname }
        map  { $_->{label} }
        sort { $b->{sim} <=> $b->{sim} } @$json;

    @dirnames_related = @dirnames_related[ 0 .. $n ];

    my $books_rs = $self->result_source->resultset->search(
        { 'me.dirname' => { 'in' => \@dirnames_related }, },
        {   select => [
                \"me.dirname AS dirname",
                \"me.startseite AS startseite",
                \"me.dta_bibl_angabe AS dta_bibl_angabe"
            ],
            as => [qw/ dirname startseite dta_bibl_angabe /],
        }
    );

    $books_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');

    while ( my $b = $books_rs->next ) {
        my $fi = first_index {/$b->{dirname}/} @dirnames_related;
        @semantic_related_books[$fi] = $b;
    }
    return \@semantic_related_books;
}

sub dta_pub_location_fw {
    my $self  = shift;
    my $place = $self->dta_pub_location;
    return unless $place;
    my @places = split /\s*;\s*/, $place;
    return \@places;
}

sub publisher_fw {
    my $self = shift;
    return $self->_parse_name_field( $self->publisher );
}

sub translator_fw {
    my $self = shift;
    return $self->_parse_name_field( $self->uebersetzer );
}

sub _parse_name_field {
    my ( $self, $name ) = @_;
    return unless $name;
    for ($name) {
        s/^\s*|\s*$//g;
    }
    return unless $name;
    my @items = split /\s*;\s*/, $name;
    my @ret;
    foreach my $item (@items) {
        my ( $pnd, $forename, $surname, $addname );
        if ( $item =~ s/#(\w+)// ) {
            $pnd = $1;
        }
        if ( $item =~ /,/ ) {
            ( $surname, $forename ) = split /\s*,\s*/, $item;
        }
        else {
            $addname = $item;
        }
        push @ret,
            {
            pnd      => $pnd,
            forename => $forename,
            surname  => $surname,
            addname  => $addname,
            };
    }
    return \@ret;
}

sub extra_header {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{extraheaderrep} . '/%s.header.xml',
            $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

# ==============================================================================
# DTAE, see @kaskade:/opt/DTAx/kaskade/wiegand/src/dtadoc/dtae-books
# ==============================================================================
sub extra_information {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{dtae}->{etc} . '/%s.txt', $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

sub resp {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{dtae}->{etc} . '/%s.resp',
            $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

sub resp_html {
    my $self = shift;
    my $xml  = $self->resp;
    return unless $xml;

    $xml = sprintf '<TEI xmlns="http://www.tei-c.org/ns/1.0">%s</TEI>', $xml;

    my $xslt      = XML::LibXSLT->new();
    my $source    = XML::LibXML->load_xml( string => $xml );
    my $style_doc = XML::LibXML->load_xml( location => 'root/xslt/resp.xsl' );
    my $stylesheet = $xslt->parse_stylesheet($style_doc);
    my $results    = $stylesheet->transform($source);
    my $ret        = $stylesheet->output_as_chars($results);
    for ($ret) {
        s{\Q<?xml version="1.0" encoding="utf-8"?>\E}{};
        s{ xmlns:dta="urn:dta"}{}g;
        s{, , }{, }g;
    }
    return $ret;
}

sub msidentifier {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{dtae}->{etc} . '/%s.msidentifier',
            $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

sub msidentifier_html {
    my $self = shift;
    my $xml  = $self->msidentifier;
    return unless $xml;

    $xml = sprintf '<TEI xmlns="http://www.tei-c.org/ns/1.0">%s</TEI>', $xml;

    my $xslt   = XML::LibXSLT->new();
    my $source = XML::LibXML->load_xml( string => $xml );
    my $style_doc
        = XML::LibXML->load_xml( location => 'root/xslt/msidentifier.xsl' );
    my $stylesheet = $xslt->parse_stylesheet($style_doc);
    my $results    = $stylesheet->transform($source);
    return $stylesheet->output_as_chars($results);
}

sub license {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{dtae}->{etc} . '/%s.license',
            $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

sub license_html {
    my $self = shift;
    my $xml  = $self->license;
    return unless $xml;

    $xml
        = sprintf
        '<?xml version="1.0" encoding="UTF-8"?><TEI xmlns="http://www.tei-c.org/ns/1.0">%s</TEI>',
        $xml;

    my $xslt   = XML::LibXSLT->new();
    my $source = XML::LibXML->load_xml( string => $xml );
    my $style_doc
        = XML::LibXML->load_xml( location => 'root/xslt/license.xsl' );
    my $stylesheet = $xslt->parse_stylesheet($style_doc);
    my $results    = $stylesheet->transform($source);
    return $stylesheet->output_as_chars($results);
}

sub handdesc {
    my $self = shift;
    open(
        my $fh,
        '<:utf8',
        sprintf(
            DTA->config->{dtae}->{etc} . '/%s.handdesc',
            $self->dirname
        )
    ) or return;
    my $xml;
    {
        local $/;
        $xml = <$fh>;
    }
    close $fh or die $!;
    return $xml;
}

# ==============================================================================

sub chars {
    my $self  = shift;
    my $chars = DTA->model('DTADB::Page')->search(
        { book => $self->id_book, },
        {   select => [ { sum => 'chars' } ],
            as     => ['total_chars'],
        },
    );
    return $chars->first->get_column('total_chars');
}

sub in_process {
    my $self       = shift;
    my $in_process = DTA->model('DTADB::Ticket')->search(
        {   book   => $self->id_book,
            type   => 12,
            status => 1,
        },
    );
    return $in_process->first ? $in_process->first->id : undef;
}

#RFE: this works only within DTA?
sub _token_types {
    my $self = shift;

    my $cache = Cache::FileCache->new(
        {   cache_root => DTA->path_to( 'var', 'cache' ) . "",
            namespace  => 'token_types',
            default_expires_in => 60 * 60 * 24 * 7,    # 1 week
        }
    );
    my $token = $cache->get( 'tt_' . $self->id_book );

    if ( !$token ) {
        my $txtdir = sprintf( DTA->config->{ramdisk} . '/%s/*.txt',
            $self->dirname );
        my @token
            = `/usr/local/bin/waste -stxd -a /usr/local/share/waste/de-dta-dtiger/abbr.lex -j /usr/local/share/waste/de-dta-dtiger/conj.lex -w /usr/local/share/waste/de-dta-dtiger/stop.lex --model=/usr/local/share/waste/de-dta-dtiger/model.hmm $txtdir 2>/dev/null`;

        @token = grep {/[[:alnum:]]/} map { utf8::decode($_); $_ } @token;

        $token->{__dta_token} = scalar @token;
        $token->{__dta_types} = scalar uniq @token;
        $cache->set( 'tt_' . $self->id_book, $token )
            unless scalar @token == 0;
    }
    while ( my ( $k, $v ) = each %$token ) {
        $self->{$k} = $v;
    }
}

sub token {
    my $self = shift;
    _token_types($self);
    return $self->{__dta_token};
}

sub types {
    my $self = shift;
    _token_types($self);
    return $self->{__dta_types};
}

sub urn {
    my $self = shift;
    return unless $self->ready =~ /^[12]$/;

    if ( defined $self->doi && length $self->doi ) {
        return $self->doi;
    }
    else {
        my $urn
            = URN::gen( sprintf 'urn:nbn:de:kobv:b4-%d-', $self->id_book );
        $self->doi($urn);
        $self->update;
        return $self->doi;
    }
}

=head2 coins

=cut

sub coins {
    my ( $self, $page ) = @_;

    my $coins = Metadata::COinS->new;
    $coins->set( 'btitle' => $self->title )->set( 'title' => $self->title )
        ->set( 'date'    => $self->dta_pub_date )
        ->set( 'edition' => ( $self->dta_auflage || '1. Auflage' ) );

    if ( $self->dta_pub_location ) {
        $coins->set( 'place' => $self->dta_pub_location );
    }

    if ( $self->dta_pub_verlag ) {
        $coins->set( 'pub' => $self->dta_pub_verlag );
    }

    if ( $self->umfang ) {
        $coins->set( 'tpages' => $self->umfang );
    }

    if ( $self->dta_reihe_titel ) {
        $coins->set( 'series' => $self->dta_reihe_titel );
    }

    if ($page) {
        $coins->set( 'pages' => $page )->set( 'genre' => 'bookitem' )
            ->set( 'chapter' => 'test' );
    }
    else {
        $coins->set( 'genre' => 'book' );
    }

    my @authors;
    foreach my $i ( 1 .. 3 ) {
        my $field_sur = "autor$i" . "_lastname";
        my $field_pre = "autor$i" . "_prename";
        last unless $self->$field_sur;
        my $au = $self->$field_sur;
        $au = join( ' ', $self->$field_pre, $self->$field_sur )
            if $self->$field_pre;
        $coins->set( 'au' => $au );
    }
    return $coins;
}


=head2 pages_recto_verso

Creates a hash indicating if text has been written or printed on the
"right" or "front" side and on the "back" side of a leaf of paper
see: https://en.wikipedia.org/wiki/Recto_and_verso

{
  '0005' => '[3r]',
  '0003' => '[2r]',
  '0004' => '[2v]',
  '0001' => '[1r]',
  '0002' => '[1v]'
};

=cut

sub pages_recto_verso {
    my ($self) = @_;
    my $pages = DTA->model('DTADB::Page')
        ->search( { book => $self->id_book, }, { order_by => 'page', } );
    my %pages_recto_verso;
    while ( my $rs = $pages->next ) {
        $pages_recto_verso{ sprintf "%04d", $rs->page } = $rs->n if $rs->n;
    }
    return \%pages_recto_verso;
}

sub file_dta_xml{
    my ($self) = @_;
    return sprintf("%s/%s.txt.xml", DTA->config->{xml_origin}, $self->dirname);
}

=head2

file-path to the complete dta-xml
- xml-model processing instruction is 'basisformat_ohne_header.rng'
- without TEI header, uses dummy header for validation
- no HEX entities

=cut

sub schematron_schema_url{
    my ($self) = @_;
    return 'http://www.deutschestextarchiv.de/basisformat.sch' if $self->relax_ng_schema eq 'basisformat.rng';
}


sub file_xml_origin{
    my ($self) = @_;
    return sprintf("%s/%s.TEI-P5.xml", DTA->config->{xml_origin}, $self->dirname);
}

=head2

file-path to the full xml
- full XML with TEI header
- xml declaration without encoding
- no xml-model processing instruction
- might have deprecated/historical c tags
- HEX entities

=cut

sub file_full_staging{
    my ($self) = @_;
    return sprintf("%s/%s.TEI-P5.xml", DTA->config->{xml_staging}, $self->dirname);
}

=head2

file-path to the final xml
- full XML with TEI header
- xml declaration with encoding
- with xml-model processing instruction
- without deprecated/historical c tags
- no HEX entities

=cut

sub file_xml_release{
	my ($self) = @_;
	return sprintf("%s/%s.TEI-P5.xml", DTA->config->{xml_release}, $self->dirname);
}

__PACKAGE__->meta->make_immutable;
1;
__END__

=head1 AUTHOR

Frank Wiegand

Markus Raulf <markus.raulf@bbaw.de>

=head1 LICENSE

This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
