package Orangutan::Config;

use strict;

use Data::Dumper;

sub new {
    my $class = shift;

    my $self = { };

    my %args = ( );
    while ($#_ >= 0) {
        $args{lc(pop)} = pop;
    }

    bless $self, $class;

    foreach my $arg (keys %args) {
        if ($arg eq 'file') {
            $self->{'file'} = $args{$arg};
        }
    }

    $self->{'monkeys'} = { };
    $self->{'sections'} = { };
    $self->{'contexts'} = { };
    $self->{'trackers'} = { };
    $self->{'statuses'} = { };
    $self->{'priorities'} = { };
    $self->{'customfields'} = { };
    $self->Load;

    return $self;
}

sub Load($) {
    my $self = shift;

    if (open(CFG, '<', $self->{'file'})) {
        while (<CFG>) {
            if (/^([a-z]+)\.([a-z_]+)\s*=\s*(?:(\S+)|"([^"]+)")\s*(?:#.*)?\n$/i) {
                my ($section, $property, $value, $phrase) = ($1, $2, $3, $4);
                if (!defined($value)) {
                    $value = $phrase;
                }
                if (!defined($self->{'sections'}->{$section})) {
                    $self->{'sections'}->{$section} = { };
                }
                if (defined($self->{'sections'}->{$section}->{$property})) {
                    if (ref $self->{'sections'}->{$section}->{$property} ne 'ARRAY') {
                        $self->{'sections'}->{$section}->{$property} = [ $self->{'sections'}->{$section}->{$property} ];
                    }
                    push(@{$self->{'sections'}->{$section}->{$property}}, $value);
                } else {
                    $self->{'sections'}->{$section}->{$property} = $value;
                }
            } elsif (/^(?:#.*)?\n$/) {
                next;
            }
        }
        close(CFG);
    } else {
        die("could not open config file\n");
    }
}

sub InitContexts($) {
    my $self = shift;
    my $path = $self->Get('contexts', 'directory');
    if (defined($path)) {
        if (opendir(DIR, $path)) {
            my $count = 0;
            while (defined(my $filename = readdir(DIR))) {
                if ($filename =~ /^(.+)\.context$/) {
                    my $context = $1;
                    if (defined($self->{'sections'}->{'contexts'}) &&
                        defined($self->{'sections'}->{'contexts'}->{'exclude'}) &&
                        grep(($context eq $_), @{$self->{'sections'}->{'contexts'}->{'exclude'}})) {
                        $main::logger->Log("ignoring context %s", $context);
                        next;
                    }
                    my $result = do "$path/$filename";
                    if (defined($result)) {
                        if (!defined($self->{'contexts'}->{$context})) {
                            if (ref $result eq 'Orangutan::Context') {
                                $result->SetID($context);
                                $self->{'contexts'}->{$context} = $result;
                                $count++;
                            } else {
                                $main::logger->Log("context %s returns invalid reference", $context);
                            }
                        } else {
                            $main::logger->Log("context %s has been already loaded", $context);
                        }
                    } else {
                        if ($@) {
                            my $error = $@;
                            $error =~ s!\n$!!g;
                            $error =~ s!\\!\\\\!g;
                            $error =~ s!\n!\\n!g;
                            $main::logger->Log("could not load %s: %s", $context, $error);
                        } else {
                            $main::logger->Log("cound not load %s/%s", $path, $filename);
                        }
                    }
                }
            }
            closedir(DIR);
            $main::logger->Log("loaded %d contexts", $count);
        } else {
            $main::logger->Log("could not open contexts directory");
        }
    } else {
        $main::logger->Log("no contexts directory was specified");
    }
}

sub InitMonkeys($) {
    my $self = shift;
    my $path = $self->Get('monkeys', 'directory');
    if (defined($path)) {
        if (opendir(DIR, $path)) {
            my $count = 0;
            while (defined(my $filename = readdir(DIR))) {
                if ($filename =~ /^(.+)\.monkey$/) {
                    my $monkey = $1;
                    my $result = do "$path/$filename";
                    if (defined($result)) {
                        if (!defined($self->{'monkeys'}->{$monkey})) {
                            if (ref $result eq 'Orangutan::Monkey') {
                                $self->{'monkeys'}->{$monkey} = $result;
                                $count++;
                            } else {
                                $main::logger->Log("monkey %s returns invalid reference", $monkey);
                            }
                        } else {
                            $main::logger->Log("monkey %s has been already loaded", $monkey);
                        }
                    } else {
                        if ($@) {
                            my $error = $@;
                            $error =~ s!\n$!!g;
                            $error =~ s!\\!\\\\!g;
                            $error =~ s!\n!\\n!g;
                            $main::logger->Log("could not load %s: %s", $monkey, $error);
                        } else {
                            $main::logger->Log("could not load %s/%s", $path, $filename);
                        }
                    }
                }
            }
            closedir(DIR);
            $main::logger->Log("loaded %d monkeys", $count);
        } else {
            $main::logger->Log("could not open monkeys directory");
        }
    } else {
        $main::logger->Log("no monkeys directory was specified");
    }
}

sub Init($) {
    my $self = shift;

    if ($main::query->GetTrackers) {
        while (defined(my $tracker = $main::query->Next)) {
            $self->{'trackers'}->{lc($tracker->{'name'})} = $tracker->{'id'};
        }
        my @trackers = ( );
        foreach my $tracker (sort { length($b) <=> length($a) } keys %{$self->{'trackers'}}) {
            if ($tracker =~ /^[a-z 0-9\-]+$/i) {
                push(@trackers, $tracker);
            } else {
                $main::logger->Log("ignoring tracker: %s", $tracker);
            }
        }
        $self->{'sections'}->{'redmine'}->{'_trackers_regexp'} = join('|', @trackers);
    } else {
        $main::logger->Log("failed to fetch trackers");
    }

    if ($main::query->GetStatuses) {
        while (defined(my $status = $main::query->Next)) {
            $self->{'statuses'}->{lc($status->{'name'})} = $status->{'id'};
        }
        my @statuses = ( );
        foreach my $status (sort { length($b) <=> length($a) } keys %{$self->{'statuses'}}) {
            if ($status =~ /^[a-z 0-9]+$/i) {
                push(@statuses, $status);
            } else {
                $main::logger->Log("ignoring status: %s", $status);
            }
        }
        $self->{'sections'}->{'redmine'}->{'_statuses_regexp'} = join('|', @statuses);
    } else {
        $main::logger->Log("failed to fetch statuses");
    }

    if ($main::query->GetPriorities) {
        while (defined(my $priority = $main::query->Next)) {
            $self->{'priorities'}->{lc($priority->{'name'})} = $priority->{'id'};
        }
        my @priorities = ( );
        foreach my $priority (sort { length($b) <=> length($a) } keys %{$self->{'priorities'}}) {
            if ($priority =~ /^[a-z 0-9]+$/i) {
                push(@priorities, $priority);
            } else {
                $main::logger->Log("ignoring priority: %s", $priority);
            }
        }
        $self->{'sections'}->{'redmine'}->{'_priorities_regexp'} = join('|', @priorities);
    } else {
        $main::logger->Log("failed to fetch priorities");
    }

    if ($main::query->GetCustomFields) {
        while (defined(my $customfield = $main::query->Next)) {
            $self->{'customfields'}->{lc($customfield->{'name'})} = $customfield->{'id'};
        }
        my @customfields = ( );
        foreach my $customfield (sort { length($b) <=> length($a) } keys %{$self->{'customfields'}}) {
            if ($customfield =~ /^[a-z 0-9]+$/i) {
                push(@customfields, $customfield);
            } else {
                $main::logger->Log("ignoring custom field: %s", $customfield);
            }
        }
        $self->{'sections'}->{'redmine'}->{'_customfields_regexp'} = join('|', @customfields);
    } else {
        $main::logger->Log("failed to fetch custom fields");
    }

    if (defined($self->{'sections'}->{'statuses'})) {
        foreach my $type (keys %{$self->{'sections'}->{'statuses'}}) {
            if (ref $self->{'sections'}->{'statuses'}->{$type} eq 'ARRAY') {
                my @ids = ( );
                foreach my $name (@{$self->{'sections'}->{'statuses'}->{$type}}) {
                    my $id = $self->StatusID($name);
                    if (defined($id)) {
                        push(@ids, $id);
                    } else {
                        $main::logger->Log("unknown %s status: %s", $type, $name);
                    }
                }
                if (scalar @ids > 0) {
                    $self->{'sections'}->{'statuses'}->{$type} = \@ids;
                } else {
                    delete($self->{'sections'}->{'statuses'}->{$type});
                }
            } else {
                my $id = $self->StatusID($self->{'sections'}->{'statuses'}->{$type});
                if (defined($id)) {
                    $self->{'sections'}->{'statuses'}->{$type} = $id;
                } else {
                    $main::logger->Log("unknown %s status: %s",
                        $type, $self->{'sections'}->{'statuses'}->{$type});
                    delete($self->{'sections'}->{'statuses'}->{$type});
                }
            }
        }
    }
}

sub Get($$$) {
    my ($self, $section, $property) = @_;
    if (defined($self->{'sections'}->{$section})) {
        if (defined($self->{'sections'}->{$section}->{$property})) {
            return $self->{'sections'}->{$section}->{$property};
        } else {
            #$main::logger->Log("configuration property $section.$property does not exist");
        }
    } else {
        $main::logger->Log("configuration section $section does not exist");
    }
    return undef;
}

sub Context($$) {
    my ($self, $id) = @_;
    if (defined($self->{'contexts'}->{$id})) {
        return $self->{'contexts'}->{$id};
    } else {
        $main::logger->Log("no such context: %s", $id);
    }
}

sub Contexts($) {
    my $self = shift;
    return values %{$self->{'contexts'}};
}

sub Monkeys($) {
    my $self = shift;
    return values %{$self->{'monkeys'}};
}

sub TrackerID($$) {
    my ($self, $name) = @_;
    return $self->{'trackers'}->{lc($name)};
}

sub StatusID($$) {
    my ($self, $name) = @_;
    return $self->{'statuses'}->{lc($name)};
}

sub PriorityID($$) {
    my ($self, $name) = @_;
    return $self->{'priorities'}->{lc($name)};
}

1;
