package Mojolicious::Che;
use Mojo::Base::Che; # 郋迡邽郇 郈訄��� 迡郅�� �訄郱郋赲
use Mojo::Base  'Mojolicious';
use Mojo::Log::Che;
use Mojo::Loader qw(load_class);
#~ use Mojo::Util qw(url_unescape);
#~ use Scalar::Util 'weaken';

sub new {
  my ($class, %args) = @_;
  my $config = delete($args{config}) || 'Config.pm';
  my $app = $class->SUPER::new(%args);
  
  $app->plugin(Config =>{file => $config});
  
  #~ return $app # 郋��訄郇郋赲郕訄 邽郅邽 迡赲郋邿郇郋邿 郈迮�迮郱訄郈��郕 kill -USR2
    #~ if $ENV{HYPNOTOAD_PID} || $ENV{HYPNOTOAD_STOP};
  
  my $conf = $app->config;
  $conf->{mojo} ||= {};
  
  my $defaults = $conf->{'mojo_defaults'} || $conf->{'mojo'}{'defaults'}  || $conf->{'mojo.defaults'};
  $app->defaults($defaults)
    if $defaults;
  
  my $secret = $conf->{'mojo_secret'} || $conf->{'mojo_secrets'} || $conf->{'mojo'}{'secret'} || $conf->{'mojo'}{'secrets'} || $conf->{'mojo.secret'} || $conf->{'mojo.secrets'} || $conf->{'��解����'} || [rand];
  $app->secrets($secret);
  
  my $mode = $conf->{'mojo_mode'} || $conf->{'mojo'}{'mode'} || $conf->{'mojo.mode'};
  $app->mode($mode) # 苳訄邿郅 郅郋迣訄 �迠迮 郇迮 郈迮�迮郕郅�耯箮解��
    if $mode;
  #~ $app->log->level( $conf->{'mojo_log_level'} || $conf->{'mojo'}{'log_level'} || 'debug');
  my $log = $conf->{'mojo_log'} || $conf->{'mojo.log'} || $conf->{'mojo'}{'log'};
  $app->log(Mojo::Log::Che->new(%$log))
    if $log;
  #~ warn "Mode: ", $app->mode, "; log level: ", $app->log->level;
  
  my $home = $app->home;
  my $statics = $conf->{'mojo_static_paths'} || $conf->{'mojo.static.paths'} || $conf->{'mojo'}{'static'}{'paths'} || [];
   #~ push @{$app->static->paths}, @{$paths} if $paths;
  push @{$app->static->paths},  $home->rel_file($_) for @$statics;
  
  my $templates_paths = $conf->{'mojo_renderer_paths'} || $conf->{'mojo.renderer.paths'} || $conf->{'mojo'}{'renderer'}{'paths'} || [];
  push @{$app->renderer->paths}, $home->rel_dir($_) for @$templates_paths;
  
  my $renderer_classes = $conf->{'mojo_renderer_classes'} || $conf->{'mojo.renderer.classes'} || $conf->{'mojo'}{'renderer'}{'classes'} || [];
  push @{$app->renderer->classes}, $_ for grep ! load_class($_), @$renderer_classes;
  
  $app->�迮��邽��();
  $app->�訄郱��();
  #~ $app->訇訄郱��();
  #~ $app->郱訄郈�郋���();
  $app->郈郅�迣邽郇��();
  $app->��郕邽();
  $app->�郈迮邿���();
  $app->邾訄��������();
  $app->郱訄迡訄�箮�();
  $app->�邽郈��();
  #~ $app->minion_worker();

  return $app;

}

sub �訄郱�� { # 苭訄郱�� 邽郱 郕郋郇�邽迣訄
  my $app = shift;
  my $conf = $app->config;
  my $h = $conf->{'mojo_has'} || $conf->{'mojo.has'} || $conf->{'mojo'}{'has'} || $conf->{'�訄郱��'};
  map {
    $app->log->debug("Make the app->has('$_')");
    has $_ => $h->{$_};
  } keys %$h;
}

#~ sub 郈郅�迣邽郇�� {# �氱銑�迣邽郇�� 邽郱 郕郋郇�邽迣訄
has 郈郅�迣邽郇�� => sub {
  my $app = shift;
  my $conf = $app->config;
  my $郈郅�迣邽郇�� = {};
  my $plugins = $conf->{'mojo_plugins'} || $conf->{'mojo.plugins'} || $conf->{'mojo'}{'plugins'} || $conf->{'郈郅�迣邽郇��'}
    || return;
  map {
    push @{ $郈郅�迣邽郇��->{$_->[0]} ||= [] }, [ref $_->[1] eq 'CODE' ? $app->plugin($_->[0] => $app->${ \$_->[1] }) : $app->plugin(@$_)];
    $app->log->debug("Enable plugin [$_->[0]]");
  } @$plugins;
  return $郈郅�迣邽郇��;
};

has dbh => sub {
#~ sub 訇訄郱�� {# 郋訇�訄訇訄��氵紹匾萃� dbh 郕郋郇�邽迣訄
  my $app = shift;
  my $conf = $app->config;
  my $c_dbh = $conf->{dbh} || $conf->{'訇訄郱��'};
  return unless $c_dbh && ref($c_dbh) eq 'HASH' && keys %$c_dbh;
  #~ has dbh => sub {{};}
    #~ unless $app->can('dbh');
  
  my $dbh = {};
  
  my $req_dbi;
  while (my ($db, $opt) = each %$c_dbh) {
    if ($opt->{dbh}) {# && ref $opt eq 'DBI::db'
      $dbh->{$db} ||= $opt->{dbh};
    } else {
      ++$req_dbi
        and require DBI
        unless $req_dbi;
      $dbh->{$db} ||= DBI->connect(@{$opt->{connect}});
      $app->log->debug("苤郋迮迡邽郇邽郅��� � 訇訄郱郋邿 $opt->{connect}[0] app->dbh->{'$db'}");
    }
    
    map {
      $dbh->{$db}->do($_);
    } @{$opt->{do}} if $opt->{do};
    

  }
  return $dbh;
  
};

has sth => sub {

#~ sub 郱訄郈�郋��� {# 郋訇�訄訇訄��氵紹匾萃� sth 郕郋郇�邽迣訄
  my $app = shift;
  my $dbh = eval { $app->dbh }
    or return;
  #~ my %arg = @_;
  my $conf = $app->config;
  
  my $c_dbh = $conf->{dbh} || $conf->{'訇訄郱��'};
  my $c_sth = $conf->{sth} || $conf->{'郱訄郈�郋���'} || {};
  #~ my $c_pos = $conf->{pos} || $conf->{'郈郋���'} || {};
    
  return
    unless ($c_sth && ref($c_sth) eq 'HASH' && keys %$c_sth);
    #~ || ($c_pos && ref($c_pos) eq 'HASH' && keys %$c_pos);

  my $sth = {};
  
  while (my ($db, $opt) = each %$c_dbh) {
    while (my ($st, $sql) = each %{$opt->{sth}}) {
      $sth->{$db}{$st} = $dbh->{$db}->prepare($sql);# $app->sth->{main}{...}
      $app->log->debug("�氱憶棣創憾�郋赲邽郅��� 郱訄郈�郋� [app->sth->{$db}{$st}]");
    }
  }
  
  while (my ($db, $h) = each %$c_sth) {
    while (my ($st, $sql) = each %$h) {
      $sth->{$db}{$st} = $dbh->{$db}->prepare($sql);# $app->sth->{main}{...}
      $app->log->debug("�氱憶棣創憾�郋赲邽郅��� 郱訄郈�郋� [app->sth->{$db}{$st}]");
    }
  }
  
   $sth;
};

  
sub ��郕邽 {# 苭�郕邽 邽郱 郕郋郇�邽迣訄
  my $app = shift;
  my $conf = $app->config;
  my $hooks = $conf->{'mojo_hooks'} || $conf->{'mojo.hooks'} || $conf->{'mojo'}{'hooks'} || $conf->{'��郕邽'}
     || return;
  while (my ($name, $sub) = each %$hooks) {
    if (ref $sub eq 'ARRAY') {
      $app->hook($name => $_)
        for @$sub;
    } else {
      $app->hook($name => $sub);
    }
    
    $app->log->debug(sprintf("Applied hook%s [%s] from config", ref $sub eq 'ARRAY' ? "s (@{[ scalar @$sub]})" : '', $name));
  }

}

sub �迮��邽�� {
  my $app = shift;
  my $conf = $app->config;
  my $session = $conf->{'mojo_session'} || $conf->{'mojo_sessions'}  || $conf->{'mojo.session'}  || $conf->{'mojo.sessions'} || $conf->{'mojo'}{'session'} || $conf->{'mojo'}{'sessions'} || $conf->{'�迮��邽邽'} || $conf->{'�迮��邽��'}
    || return;
  
  #~ $app->sessions->cookie_name($session->{'cookie_name'})
    #~ if $session->{'cookie_name'};
  
  #~ $app->sessions->default_expiration($session->{'default_expiration'}) # set expiry
    #~ if defined $session->{'default_expiration'};
  
  while (my ($meth, $val) = each %$session) {
    next
      unless $app->sessions->can($meth);
    $app->sessions->$meth($val);
  }
}

sub 邾訄�������� {
  my $app = shift;
  my $conf = $app->config;
  my $routes = $conf->{'mojo_routes'} || $conf->{'mojo.routes'} || $conf->{'mojo'}{'routes'} || $conf->{'routes'} || $conf->{'邾訄��������'}
    or return;
  my $app_routes = $app->routes;
  my $apply_route = sub {
    my $r = shift || $app_routes;
    my ($meth, $arg) = @_;
    my $nr;
    if (my $m = $r->can($meth)) {
      $nr = $r->$m($arg) unless ref($arg);
      $nr = $r->$m(cb => $arg) if ref($arg) eq 'CODE';
      $nr = $r->$m(@$arg) if ref($arg) eq 'ARRAY';
      $nr = $r->$m(%$arg) if ref($arg) eq 'HASH';
      
    }  else {
      $app->log->warn("Can't method [$meth] for route",);
    }
    return $nr;
  };
  
  for my $r (@$routes) {
    my $nr = $apply_route->($app_routes, @$r[0,1])
      or next;
    $app->log->debug("Apply route [$r->[0] $r->[1]]");
    for( my $i = 2; $i < @$r; $i += 2 ) {
      $nr = $apply_route->($nr, @$r[$i, $i+1])
        or next;
    }
  }
}

sub �郈迮邿��� {
  my $app = shift;
  my $conf = $app->config;
  my $ns =  $conf->{'mojo_namespaces'} || $conf->{'mojo.namespaces'} || $conf->{'mojo'}{'namespaces'} || $conf->{'namespaces'} || $conf->{'ns'} || $conf->{'�郈迮邿���'}
    || return;
  push @{$app->routes->namespaces}, @$ns;
}

sub 郱訄迡訄�箮� {
  my $app = shift;
  my $conf = $app->config;
  my $tasks = $conf->{'jobs'} || $conf->{'tasks'} || $conf->{'郱訄迡訄�箮�'}
    or return;
  
  die "You have jobs and first enable plugin Minion"
    unless $app->renderer->get_helper('minion');
  
  while (my ($name, $sub) = each %$tasks) {
    $app->log->debug(sprintf("Applied task [%s] in [%s] from config", $name, $app->minion->add_task($name => $sub)));
  }
  #~ $app->minion->reset;
}


sub �邽郈�� {#MIME
  my $app = shift;
  my $conf = $app->config;
  my $types = $conf->{'mojo_types'}  || $conf->{'mojo.types'} || $conf->{'mojo'}{'types'} || $conf->{'types'} || $conf->{'�邽郈��'}
    or return;
  while (my ($name, $val) = each %$types) {
    $app->types->type($name => $val);
    $app->log->debug(sprintf("Applied type [%s] from config", $name));
  }
}

# overide only on my $path   = $req->url->path->to_abs_string;
sub Mojolicious::dispatch {
  my ($self, $c) = @_;

  my $plugins = $self->plugins->emit_hook(before_dispatch => $c);

  # Try to find a static file
  my $tx = $c->tx;
  $self->static->dispatch($c) and $plugins->emit_hook(after_static => $c)
    unless $tx->res->code;

  # Start timer (ignore static files)
  my $stash = $c->stash;
  $self->log->debug(sub {
    my $req    = $c->req;
    my $url = $req->url->to_abs;
    $c->helpers->timing->begin('mojo.timer');
    return sprintf qq{[%s] %s "%s://%s%s%s"},
      $req->request_id, $req->method, $url->scheme, $url->host, $url->port ? ":".$url->port : '', $url->path->to_route;
  }) unless $stash->{'mojo.static'};

  # Routes
  $plugins->emit_hook(before_routes => $c);
  $c->helpers->reply->not_found
    unless $tx->res->code || $self->routes->dispatch($c) || $tx->res->code;
}


our $VERSION = '0.0824';# as to Mojolicious/100+0.000<minor>

=pod

=encoding utf8

=head1 Mojolicious::Che

�秴憶敗�郋迣郋 赲�迮邾

癒 癒 癒 ALL GLORY TO GLORIA ! ! !

=head1 VERSION

0.0824

=head1 NAME

Mojolicious::Che - �婧憶� 訇訄郱郋赲�氵� 邾郋迡�郅�� 迡郅�� 郈�邽郅郋迠迮郇邽邿 Mojolicious. �罉�迠迮郇 �郋郅�芿碟� �訄郱赲迮�郇���氵� 郕郋郇�邽迣.

=head1 SYNOPSIS

  # app.pl
  use lib 'lib';
  use Mojo::Base 'Mojolicious::Che';
  __PACKAGE__->new(config => 'lib/Config.pm')->start();


=head1 Config file

�氱憾��觧棣憶� ���郋郕 赲 �殁�郋邾 郕郋郇�邽迣迮 �郋郋�赲迮���赲�迮� 邽�郈郋郅郇迮郇邽�� 赲 邾郋迡�郅迮!

  {
  '�𠇲�郋迮郕�'=>'苠迮��-郈�郋迮郕�',
  # mojo => {
    # defaults =>
    # secrets =>
    # mode=>
    # log => {level=>...}
    # static => {paths => [...]},
    # renderer => {paths => [...], classes => [...], },
    # session[s] =>
    # has =>
    # plugins =>
    # hooks =>
    # namespaces =>
    # routes =>
    # jobs =>
    # types =>
  # },
  # or with prefix mojo_
  # Default values for "stash" in Mojolicious::Controller, assigned for every new request.
  mojo_defaults => {layout=>'default',},
  # '��解����' => [
  mojo_secrets => ['true 123 my app',],
  mojo_mode=> 'development',
  mojo_log=>{level => 'error'},
  mojo_static_paths => ["static"],
  mojo_renderer_classes => ["Mojolicious::Foo::Fun"],
  # '�迮��邽��'(邽郅邽 �迮��邽邽) => 
  mojo_session[s] => {cookie_name => 'EXX', default_expiration => 86400},
  
  # '�訄郱��' => '�𠝹萃� 500-700 郇訄郱訄迡 訇�氵� �訄郕郋邿 迡邽郕邽邿 ��迮郈郇郋邿 �郋�迣郋赲�氵� 迠訄迡郇�氵� 郇訄�郋迡 苭�𡤕灧𡤕㘵�. 苤�郋郅邽�迮邿 邽� "迣郋��迡訄���赲訄" 訇�氵� 迣郋�郋迡 苠�芿槽��訄�訄郕訄郇��, 迣迡迮-�郋 郇訄 訇迮�迮迣� 邾郋��� �惧參�郈邽��. �氱憾�郋邾郕邽 �殁�邽� 郅�耨棣菩� �訄���氵諱匾諸� 郈郋 ���訄郇訄邾 �㘵憾��邽邽, 苤�迮迡郇迮邿 �𡤕煩覜�, �𢌡紼�郋郈��. �䢛���� 邾郇迮郇邽迮, �𢒼�郋 �訄郱訄��� 郈�邽�赲郋邽郅邽 �迮訇迮 郇訄郱赲訄郇邽迮 �𢌡𠸝㘵𢌡�, 郇郋 �殁�郋 郇迮 �迮 訇邽訇郅迮邿�郕邽迮 郕�郋�郕邽迮 迮赲�迮邽, 訄 迠訄迡郇�氵� 郈郋�郋邾郕邽 郕�郋邾訄郇�芿憶諸�迮赲'
  mojo_has => {
    foo => sub {my $app = shift; return 'is a bar';},
  },
  
  # '訇訄郱��' => 
  # will be as has!
  dbh=>{
    'main' => {
      # DBI->connect(dsn, user, passwd, $attrs)
      connect => ["DBI:Pg:dbname=test;", "postgres", undef, {
        ShowErrorStatement => 1,
        AutoCommit => 1,
        RaiseError => 1,
        PrintError => 1, 
        pg_enable_utf8 => 1,
        #mysql_enable_utf8 => 1,
        #mysql_auto_reconnect=>1,
      }],
      # or use Foo::Dbh; external defined dbh
      # dbh => Dbh->dbh,
      # will do on connect
      do => ['set datestyle to "ISO, DMY";',],
      # prepared sth will be as has $app->sth->{<dbh name>}{<sth name>}
      sth => {
        foo => <<SQL,
  select * 
  from foo
  where
    bar = ?;
  SQL
      },
    }
  },
  # '郱訄郈�郋���' => 
  # prepared sth will be as has $app->sth->{<dbh name>}{<sth name>}
  sth => {
    main => {
      now => "select now();"
    },
  },
  
  # '郈郅�迣邽郇��'=> [
  mojo_plugins=>[ 
      ['Foo::Bar'],
      [Foo::Bar::Plugin => opt1 => ..., opt2 => ...],
      ['Foo::Plugin' => sub {<...returns config data list...>}],
  ],
  # '��郕邽' => 
  mojo_hooks=>{
    #~ before_dispatch => sub {1;},
  },
  # '�郈迮邿���' => [...]
  namespaces => ['Space::Shattle'],
  # '邾訄��������' => [...]
  routes => [
    [get=>'/', to=> {cb=>sub{shift->render(format=>'txt', text=>'Hello friend!');},}],
  ]
  #~ '郱訄迡訄�箮�'=> {#first enable plugin Minion
  jobs => {
    slow_log => sub {
      my ($job, $msg) = @_;
      sleep 5;
      $job->app->log->error(qq{slow_log "$msg"});
    },
  },
  # 邽郅邽 '�邽郈��'=>{...}
  types => {
    docx => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
    ...
  },
  };

=head1 ATTRIBUTES

B<Mojolicious::Che> inherits all attributes from L<Mojolicious> and implements the
following new ones.

=head2 dbh

Set DBI handlers from config B<dbh> (邽郅邽 B<訇訄郱��>)

  my $dbh = $app->dbh->{main};

=head2 sth

Set prepared stattements from config B<sth> (邽郅邽 B<郱訄郈�郋���>).

  my $sth = $app->sth->{main}{foo};

=head2 郈郅�迣邽郇��

Apply the plugins. See L<Mojolicious#plugins>, L<Mojolicious::Plugins>.

=head1 METHODS

B<Mojolicious::Che> inherits all methods from L<Mojolicious> and implements the following new ones.

=head2 �迮��邽��()

Session object config apply. See L<Mojolicious#sessions>, L<Mojolicious::Sessions>.

=head2 �訄郱��()

Apply the has's. UTF names allow.

=head2 ��郕邽()

Apply the hooks. See L<Mojolicious#HOOKS>.

=head2 �郈迮邿���()

Apply the namespaces. Push @{$app->routes->namespaces} your namespaces. See L<Mojolicious#routes>.

  namespaces => ['Space::Shattle'],

=head2 邾訄��������()

Apply the routes. See L<Mojolicious#routes>, L<Mojolicious::Guides::Routing>.

  #~ '邾訄��������' => [
  'routes'=>[
    [get=>'/', to=> {cb=>sub{shift->render(format=>'txt', text=>'Welcome!');},}],
  ],

=head2 郱訄迡訄�箮�()

Apply the jobs. See L<Minion>.

  #~ '郱訄迡訄�箮�'=> {#first enable plugin Minion
  'jobs'=> { # or tasks
    slow_log => sub {
      my ($job, $msg) = @_;
      sleep 5;
      $job->app->log->error(qq{slow_log "$msg"});
    },
    
  },

=head �邽郈��()

Apply the new types. See L<Mojolicious#types>, L<Mojolicious::Types>.

=head1 SEE ALSO

L<Mojolicious>

L<Ado>

=head1 AUTHOR

�婧解�訄邽郅 虴迮 (Mikhail Che), C<< <mche[-at-]cpan.org> >>

=head1 BUGS / CONTRIBUTING

Please report any bugs or feature requests at L<https://github.com/mche/Mojolicious-Che/issues>.
Pull requests also welcome.

=head1 COPYRIGHT

Copyright 2016+ Mikhail Che.

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

=cut

1;