package App::EventStreamr::Config;
use Method::Signatures;
use Sys::Hostname;
use experimental 'say';
use Config::JSON; # libconfig-json-perl
use File::Path qw(make_path);
use Carp 'croak';
use App::EventStreamr::Devices;
use Moo;
use namespace::clean;

# ABSTRACT: A config object

our $VERSION = '0.5'; # VERSION: Generated by DZP::OurPkg:Version


has 'config_path'   => ( is => 'rw', default  => sub { "$ENV{HOME}/.eventstreamr" } );
has 'config_file'   => ( is => 'rw', lazy => 1, builder => 1 );
has 'roles'         => ( is => 'rw', default => sub { [ ] } );
has 'backend'       => ( is => 'rw', default => sub { "DVswitch" } );
has 'nickname'      => ( is => 'rw', lazy => 1, builder => 1 );
has 'room'          => ( is => 'rw', lazy => 1, default => sub { 'test_room' } );
has 'record_path'   => ( is => 'rw', lazy => 1, default => sub { '/tmp/$room/$date' } );
has 'run'           => ( is => 'rw', lazy => 1, default => sub { '0' } );
has 'control'       => ( is => 'rw' );
has 'stream'        => ( is => 'rw' );
has 'youtube'       => ( is => 'rw' );
has 'sync'          => ( is => 'rw' );
has 'pid'           => ( is => 'ro', lazy => 1, builder => 1);
has 'log_level'     => ( is => 'ro', default => sub { 'INFO, LOG1' } );
has 'mixer'         => ( 
  is => 'rw', 
  lazy => 1, 
  default => sub { 
    {
      host => '127.0.0.1',
      port => '1234',
    }
  }
);

# TODO: Be more intelligent with devices
has 'devices_util'      => ( is => 'rw', lazy => 1, builder => 1 );
has 'devices'           => ( is => 'rw', default => sub { [ ] } );
has 'available_devices' => ( is => 'rw' );

# TODO: this really needs to be changed to some
# type of GUID
has 'macaddress'  => ( is => 'rw', lazy => 1, builder => 1 );

# External utilities
has 'localconfig' => ( 
  is => 'rw', 
  lazy => 1, 
  builder => 1, 
  handles => [ qw( create write config ) ],
);

# DVswitch Checking - Probably better somewhere else
has 'dvswitch_running'  => ( is => 'rw', default => sub { '0' } );

sub BUILD {
  my $self = shift;
  $self->_load_config;
}

method _build_pid() {
  return $$;
}

method _build_devices_util() {
  return App::EventStreamr::Devices->new();
}

method update_devices() {
  $self->{available_devices} = $self->devices_util->all();
}

method _build_config_file() {
  return $self->config_path."/config.json";
}

method _build_nickname() {
  return hostname || "default_name";
}

method _build_macaddress() {
  my $macaddress = `ifdata -ph eth0`;
  chomp $macaddress;
  return $macaddress;
}

method _create_config_path() {
  if ( ! -d $self->config_path ) {
    make_path($self->config_path) or
      croak "Couldn't create config path $self->{config_path}";
  }
}

method _build_localconfig() {
  # Config JSON barfs if you try to load a config that doesn't exist,
  # this will load one or attempt to create it.
  if ( -e $self->config_file ) {
    return Config::JSON->new($self->config_file);
  } else {
    $self->_create_config_path();
    my $config = Config::JSON->create($self->config_file);

    $config->{config} = {
      nickname => $self->nickname,
      room => $self->room,
      record_path => $self->record_path,
      run => $self->run,
      backend => $self->backend,
    };
    $config->write;
    return $config;
  }
}

method _load_config() {
  # TODO: There has to be a better way..
  foreach my $key (keys %{$self->localconfig->{config}}) {
    $self->{$key} = $self->localconfig->{config}{$key};
  }
}

method reload_config() {
  $self->_build_localconfig;
}

method _clean_config() {
  return {
    nickname => $self->nickname,
    room => $self->room,
    record_path => $self->record_path,
    run => $self->run,
    backend => $self->backend,
    roles => $self->roles,
    stream => $self->stream,
    youtube => $self->youtube,
    sync => $self->sync,
    mixer => $self->mixer,
    control => $self->control,
    devices => $self->devices,
    backend => $self->backend,
  }
}

method _post_data() {
  my $config = $self->_clean_config();
  $config->{manager}{pid} = $$;
  $config->{macaddress} = $self->macaddress();
  return $config;
}


method write_config() {
  $self->localconfig->{config} = $self->_clean_config();
  $self->localconfig->write;
}



method configure() {
  say "Welcome to the EventStreamr config utility\n";
  
  my $answer = $self->prompt(
    "It will clear the current config, is this ok? y/n",
    "n",
  );
  exit 1 if ($answer =~ /n/i);
 
  # TODO: Write config clear method
  $self->roles([]);
  # Config Questions
  $self->configure_room();
  $self->configure_backend();
  $self->configure_mixer();
  $self->configure_ingest();
  $self->write_config();
  
  say;

  # Confirm written.
  if (-e $self->config_file) {
    say "Config written successfully";
  } else {
    say "Config failed to write";
  }
  # TODO: Stream Configuration  
  #$answer = undef;
  #$answer = $self->prompt(
  #  "Stream - stream to configured service y/n: ",
  #  "y",
  #);

  #push(@{$self->{roles}}, "stream") if ($answer =~ /y/i);
}

method configure_mixer() {
  my $answer = $self->prompt(
    "Mixer - Video mixer interface y/n",
    "y",
  );

  if ($answer =~ /y/i) {
    push(@{$self->roles}, "mixer");
    $self->configure_remote_mixer();

    # We could run gst-switch srv or dvsink
    # on separate hosts, but this will do
    # for now.
    push(@{$self->roles}, "record");
    $self->configure_recordpath();
  }
}

method configure_ingest() {
  my $answer = $self->prompt(
    "Ingest - audio/video ingest y/n",
    "y",
  );

  if ($answer =~ /y/i) {
    push(@{$self->roles}, "ingest");
    
    $self->update_devices();
    $self->devices([]);
    
    foreach my $device (@{$self->{available_devices}{array}}) {
      my $ingest = $self->prompt(
        "Enable '".$device->{name}."' for ingest",
        "y",
      );
      push(@{$self->devices}, $device) if ($ingest =~ /y/i);
    }
    $self->configure_remote_mixer() if (! $self->{mixer}{host});
  }
}

method configure_remote_mixer() {
  # TODO: Setting config like this is terrible, 
  # figure out how to do it better
  my $answer = $self->prompt(
    "host - switching host",
    "127.0.0.1",
  );
  $self->{mixer}{host} = $answer ;
  $answer = $self->prompt(
    "port - switching port",
    "1234",
  );
  $self->{mixer}{port} = $answer;
}

method configure_backend() {
  my $answer = $self->prompt(
    "backend - DVswitch|GSTswitch",
    $self->backend,
  );
  if ($answer =~ /gst/i) {
    $self->backend('GSTswitch');
    return;
  } elsif ($answer =~ /dv/i) {
    $self->backend('DVswitch');
    return;
  } else {
    say "Invalid backend";
    $self->configure_backend;
    return
  }
}

method configure_recordpath() {
  say '$room + $date can be used as variables in the path and';
  say 'will correctly be set and created at run time';
  my $answer = $self->prompt(
    "recordpath - ",
    '/tmp/$room/$date',
  );
  $self->record_path($answer);
}

method configure_room() {
  my $answer = $self->prompt(
    "Room - For record path and controller",
    $self->room,
  );
  $self->room($answer);
}

method prompt($question,$default?) { # inspired from here: http://alvinalexander.com/perl/edu/articles/pl010005
  if ($default) {
    print $question, " [", $default, "]: ";
  } else {
    print $question, ": ";
    $default = "";
  }

  $| = 1;               # flush
  $_ = <STDIN>;         # get input

  chomp;
  if ("$default") {
    return $_ ? $_ : $default;    # return $_ if it has a value
  } else {
    return $_;
  }
}

with('App::EventStreamr::Roles::Logger','App::EventStreamr::Roles::ConfigAPI');

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::EventStreamr::Config - A config object

=head1 VERSION

version 0.5

=head1 SYNOPSIS

This package provides core configuration methods. Class
will attempt to load the config via a number of different
methods and fall back to a default state.

=head1 DESCRIPTION

Provides access to configuration methods.

=head1 METHODS

=head2 write_config

  $config->write_config();

Will write the config out to disk.

=head2 configure

  $config->configure();

Will run through configuring EventStreamr.

=head1 AUTHOR

Leon Wright < techman@cpan.org >

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2014 by Leon Wright.

This is free software, licensed under:

  The GNU Affero General Public License, Version 3, November 2007

=cut
