#!/usr/bin/perl
# co2server.pl - A simple JSON REST API for CO2 data
# launch co2server.pl and access http://localhost:8082/weekly
#
# Created by Farid Zehetbauer <farid(at)hostmaster.org>
# for http://www.riseflyorbit.org/climate/calculator.html
# 
# Use mod_rewrite and mod_proxy for Apache HTTP Server
# RewriteRule "^/co2/(.*)" "http://127.0.0.1:8082/$1" "[proxy]"
#
# This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 
# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter 
# to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
use FindBin;
chdir "${FindBin::Bin}";

use HTTP::Daemon;
use IO::Handle;
use IO::File;
use Time::HiRes qw//;

use sigtrap;

use strict;
use warnings;

unless (-f 'co2_weekly_mlo.txt') {
    use LWP::Simple;
    LWP::Simple::mirror('ftp://aftp.cmdl.noaa.gov/products/trends/co2/co2_weekly_mlo.txt', 'co2_weekly_mlo.txt');
}
my $data;
if (my $fh = IO::File->new('co2_weekly_mlo.txt')) {
    LINE: while (my $line = <$fh>) {
        $line =~ s/^\s+//;
        next LINE if $line =~ /^#/;
        my @line = split(/\s+/, $line);
        unless (scalar(@line) == 9) {
            warn $line;
            next LINE;
        }
        unless ($line[4] > 0) {
            warn $line;
            next LINE;
        }
        push @{$data}, {
            date => sprintf("%04u-%02u-%02u", $line[0], $line[1], $line[2]),
            ppm => $line[4],
        };
    }
}
my $daemon = HTTP::Daemon->new(
    LocalPort => 8082,
    ReuseAddr => 1,
);

$SIG{CHLD} = 'IGNORE';
while (my $client = $daemon->accept) {
    my $pid = fork();
    if ($pid > 0) {
        # parent
    } elsif ($pid == 0) {
        http_client($client);
        last;
    } else {
        warn "failed to fork: $!" unless defined($pid);
    }
}

sub http_client {
    my ($client) = @_;

    $SIG{PIPE} = sub {
        die "SIGPIPE!";
    };
    REQUEST: while (my $request = $client->get_request) {
        $client->force_last_request;
        my $t = Time::HiRes::time();

        my $params = { $request->uri->query_form };
        my $start = "1974-05-19";
        my @gmt = gmtime();
        my $end = sprintf("%04u-%02u-%02u", 1900 + $gmt[5], $gmt[4] + 1, $gmt[3]);
        if ($params->{start} && $params->{start} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) {
            $start = sprintf("%04u-%02u-%02u", $1, $2, $3);
        }
        if ($params->{end} && $params->{end} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) {
            $end = sprintf("%04u-%02u-%02u", $1, $2, $3);
        }

        STDOUT->printf("%s %s ", $client->peerhost, $request->uri->path);
        my $result;
        if ($request->uri->path =~ /^\/weekly$/) {
            SAMPLE: for my $sa (@{$data}) {
                if ($sa->{date} le $start) {
                    $result = [ $sa ];
                } elsif ($sa->{date} le $end) {
                    push @{$result}, $sa;
                } else {
                    last SAMPLE;
                }
            }
        }
        if ($result) {
            my $ppm = 0;
            for my $sa (@{$result}) {
                $ppm += $sa->{ppm};
            }
            $ppm /= scalar(@{$result});
            my $volume = 5.1e18 / 1.275; # mass of atmosphere, density of air
            $volume = $volume * ($ppm / 1000000);
            my $mass = $volume * 1.89; # density of CO2
            my $surface = 5.1e8 * 1e6; # 5.1e8km2 to m2
            my $pressure = $mass * 9.807 / $surface;
            my $mass_per_sqm = $mass / $surface;
            my $thickness = $volume / $surface;
            $result = {
                ppm => sprintf("%.2f", $ppm),
                volume => sprintf("%.3e", $volume),
                mass => sprintf("%.3e", $mass),
                pressure => sprintf("%.0f", $pressure),
                mass_per_sqm => sprintf("%.3f", $mass_per_sqm),
                thickness => sprintf("%.2f", $thickness),
                samples => $result,
            };
            use JSON;
            my $json = JSON->new->canonical->pretty->utf8;
            $result = $json->encode($result);
            $client->send_response(
                HTTP::Response->new(
                    200,
                    "OK",
                    HTTP::Headers->new(
                        'Content-Type' => 'text/json',
                    ),
                    $result
                )
            );
        } else {
            $client->send_error(404, "NOT FOUND");
        }
        STDOUT->printf("%.3fs\n", Time::HiRes::time() - $t);
    }
    $client->close;
}
