package Dancer::Serializer::Mutable;

use strict;
use warnings;

use base 'Dancer::Serializer::Abstract';
use Dancer::SharedData;

my $serializer = {
    'text/x-yaml'      => 'YAML',
    'text/html'        => 'YAML',
    'text/xml'         => 'XML',
    'text/x-json'      => 'JSON',
    'application/json' => 'JSON',
};

my $loaded_serializer = {};
my $_content_type;

sub _find_content_type {
    my ($self, $request) = @_;

    # first content type, second accept and final default
    my %content_types;
    my $params;

    if ($request) {
        $params = $request->params;
    }

    my $method = $request->method;

    if ($method =~ /^(?:POST|PUT)$/) {
        if ($request->{content_type}) {
            $content_types{$request->{content_type}} = 4;
        }

        if ($params && $params->{content_type}) {
            $content_types{$params->{content_type}} = 3;
        }
    }

    if ($request->{accept}) {
        $content_types{$request->{accept}} = 2;
    }
    if ($request->{'accept_type'}) {
        $content_types{$request->{accept_type}} = 1;
    }

    $content_types{'application/json'} = 0;

    return [
        sort { $content_types{$b} <=> $content_types{$a} }
          keys %content_types
    ];
}

sub serialize {
    my ($self, $entity) = @_;
    my $request    = Dancer::SharedData->request;
    my $serializer = $self->_load_serializer($request);
    return $serializer->serialize($entity);
}

sub deserialize {
    my ($self, $content) = @_;
    my $request    = Dancer::SharedData->request;
    my $serializer = $self->_load_serializer($request);
    return $serializer->deserialize($content);
}

sub content_type {
    my $self = shift;
    $_content_type;
}

sub support_content_type {
    my ($self, $ct) = @_;
    grep /^$ct$/, keys %$serializer;
}

sub _load_serializer {
    my ($self, $request) = @_;

    my $content_types = $self->_find_content_type($request);
    foreach my $ct (@$content_types) {
        if (exists $serializer->{$ct}) {
            my $module = "Dancer::Serializer::" . $serializer->{$ct};
            if (!exists $loaded_serializer->{$module}) {
                if (Dancer::ModuleLoader->load($module)) {
                    my $serializer_object = $module->new;
                    $loaded_serializer->{$module} = $serializer_object;
                }
            }
            $_content_type = $ct;
            return $loaded_serializer->{$module};
        }
    }
}

1;
__END__

=head1 NAME

Dancer::Serializer::Mutable - (De)Serialize content using the appropriate HTTP header

=head1 SYNOPSIS

=head1 DESCRIPTION

This serializer will try find the best (de)serializer for a given request.
For this, it will go through:

=over 4

=item

The B<content_type> from the request headers

=item

the B<content_type> parameter from the URL

=item

the B<accept> from the request headers

=item

The default is B<application/json>

=back

=head2 METHODS

=over 4

=item B<serialize>

Serialize a data structure to a YAML structure.

=item B<deserialize>

Deserialize a YAML structure to a data structure

=item B<content_type>

Return 'application/json'

=back
