#!/usr/bin/env python
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import argparse
import boto
import boto.s3
import boto.s3.key
import hashlib
import json
import os.path
from sh import git, head
import sys
import zipfile

class PebbleBundle(object):
    MANIFEST_FILENAME = 'manifest.json'

    def __init__(self, bundle_path):
        bundle_abs_path = os.path.abspath(bundle_path)
        if not os.path.exists(bundle_abs_path):
            raise "Bundle does not exist: " + bundle_path

        self.zip = zipfile.ZipFile(bundle_abs_path)
        self.path = bundle_abs_path
        self.manifest = None

    def get_manifest(self):
        if (self.manifest):
            return self.manifest

        if self.MANIFEST_FILENAME not in self.zip.namelist():
            raise "Could not find {}; are you sure this is a PebbleBundle?".format(self.MANIFEST_FILENAME)

        self.manifest = json.loads(self.zip.read(self.MANIFEST_FILENAME))
        return self.manifest

    def is_firmware_bundle(self):
        return 'firmware' in self.get_manifest()

    def get_firmware_info(self):
        if not self.is_firmware_bundle():
            return None

        return self.get_manifest()['firmware']


S3_PUBLIC_URL = 'https://pebblefw.s3.amazonaws.com/'
S3_BUCKET = 'pebblefw'
LATEST_MANIFEST = 'latest.json'
BUNDLE_DIRECTORY = 'pbz'
LAYOUTS_DIRECTORY = 'layouts'

RECOVERY_MANIFESTS = {
        "bigboard" : {
            "friendlyVersion" : "v1.4",
            "timestamp" : 1354827516,
            "sha-256" : "61f59c29b7e1c72c2e4de6848affb56ddd1f4c79512bfb9325aa99a650105d19",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/bigboard/release/pbz/recovery_bigboard_v1.4.pbz",
            "notes" : "Bigboard Recovery Firmware"
            },
        "ev2_4" : {
            "friendlyVersion" : "v1.5.2",
            "timestamp" : 1356919500,
            "sha-256" : "1ca285d65d80b48b90bab85c5f9e54c907414adffa6f1168beec8aac5d6f32a2",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/ev2_4/release/pbz/recovery_ev2_4_v1.5.2.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "bb2" : {
            "friendlyVersion" : "v1.5.5",
            "timestamp" : 1377981535,
            "sha-256" : "620cfcadc8d28240048ffa01eb6984b06774a584349f178564e1548ecc813903",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/bb2/release/pbz/recovery_bb2_v1.5.5.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "v1_5" : {
            "friendlyVersion" : "v1.5.5",
            "timestamp" : 1377981535,
            "sha-256" : "d952ffc4b46d1db965ac8ff0cffca645c1f9c9a8f296cf5da452f698b5bc15ce",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/v1_5/release/pbz/recovery_v1_5_v1.5.5.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "v2_0" : {
            "friendlyVersion" : "v1.5.5",
            "timestamp" : 1377981535,
            "sha-256" : "07fd7c6ef5a1fbf51b8122bca3e72772015b5f210e905f30e853c65ae02ee09b",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/v2_0/release/pbz/recovery_v2_0_v1.5.5.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "snowy_evt2" : {
            "friendlyVersion" : "v3.0.2-prf",
            "timestamp" : 1430231686,
            "sha-256" : "5579978e73db5d57831d4e1b8e3416b66fe94f5f19d3eb3ac49fcd54270935f3",
            "url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_evt2/release-v3/pbz/recovery_snowy_evt2_v3.0.2-prf.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "snowy_dvt" : {
            "friendlyVersion" : "v3.0.2-prf",
            "timestamp" : 1430231686,
            "sha-256" : "3338d94af5b97e94fe0e0dee5eddae7ea9c8f299b64070d1d1a692948c651a02",
            "url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_dvt/release-v3/pbz/recovery_snowy_dvt_v3.0.2-prf.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "snowy_s3" : {
            "friendlyVersion" : "v3.0.3-prf",
            "timestamp" : 1435167301,
            "sha-256" : "0e5bd3d327c5180506b20ef33aed1601037df0030683b27d68c16e4836c4def0",
            "url" : "http://pebblefw.s3.amazonaws.com/pebble/snowy_s3/release-v3/pbz/recovery_snowy_s3_v3.0.3-prf.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "spalding" : {
            "friendlyVersion" : "v3.2-prf5",
            "timestamp" : 1440968436,
            "sha-256" : "f6d3616b423d32618a1a3615ae8f8d5526941c7b6311be45e4387045c7eb7602",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/spalding/release-v3/pbz/recovery_s4_pvt_prf5.pbz",
            "notes" : "Official Recovery Firmware"
            },
        "spalding_evt" : {
            "friendlyVersion" : "v3.2-prf5",
            "sha-256": "f9c2bb857952c034c7122b67f5178a25fcdf9ef8cce3d3bd8c2c8671676958c2",
            "url" : "https://pebblefw.s3.amazonaws.com/pebble/spalding/release-v3/pbz/recovery_s4_evt_prf5.pbz",
            "timestamp": 1440968436,
            "notes" : "Official Recovery Firmware"
            }
        }

def build_s3_path(*args):
    return 'pebble/' + '/'.join(args)

def __git_tag():
    return str(git.describe()).strip()

def generate_update_manifest(bundle, bundle_s3_key, layouts_s3_key, notes):
    bundle_path = os.path.abspath(bundle.path)
    board = bundle.get_firmware_info()['hwrev']

    timestamp = bundle.get_firmware_info()['timestamp']

    with open(bundle_path, 'rb') as normal_bundle:
        bundle_data = normal_bundle.read()
        sha256 = hashlib.sha256(bundle_data).hexdigest()

    bundle_url = S3_PUBLIC_URL + bundle_s3_key
    layouts_url = S3_PUBLIC_URL + layouts_s3_key

    manifest = {
        'normal' : {
            'friendlyVersion' : __git_tag(),
            'timestamp' : timestamp,
            'sha-256' : sha256,
            'url' : bundle_url,
            'layouts' : layouts_url,
            'notes' : notes
            },
        'recovery' : RECOVERY_MANIFESTS[board]
        }

    return json.dumps(manifest)

def push_to_s3(bundle_path, layouts_path, stage, notes, update_latest=False, dry_run=False):
    conn = boto.connect_s3()

    bundle = PebbleBundle(bundle_path)
    bundle_path = bundle.path
    bundle_filename = os.path.basename(bundle_path)

    git_tag = str(git.describe()).strip()
    board = bundle.get_firmware_info()['hwrev']
    bundle_key = build_s3_path(board, stage, BUNDLE_DIRECTORY, bundle_filename)
    layouts_key = build_s3_path(board, stage, LAYOUTS_DIRECTORY, __git_tag() + '_layouts.json')
    manifest_key = build_s3_path(board, stage, __git_tag() + '.json')
    with open(notes, 'r') as f:
        notes_txt = f.read().strip()

    manifest_contents = generate_update_manifest(bundle, bundle_key, layouts_key, notes_txt)

    if dry_run:
        print manifest_contents
        return

    fw_bucket = boto.s3.bucket.Bucket(conn, S3_BUCKET)

    # Upload the bundle to S3
    bundle_obj = boto.s3.key.Key(fw_bucket, bundle_key)
    bundle_obj.set_contents_from_filename(bundle_path)
    bundle_obj.set_acl('public-read')
    print 'PUT ' + bundle_key

    bundle_obj = boto.s3.key.Key(fw_bucket, layouts_key)
    bundle_obj.set_contents_from_filename(layouts_path)
    bundle_obj.set_acl('public-read')
    print 'PUT ' + layouts_key

    manifest_obj = boto.s3.key.Key(fw_bucket, manifest_key)
    manifest_obj.set_contents_from_string(manifest_contents)
    manifest_obj.set_acl('public-read')
    print 'PUT ' + manifest_key

    if update_latest:
        latest_key = build_s3_path(board, stage, LATEST_MANIFEST)
        latest_obj = boto.s3.key.Key(fw_bucket, latest_key)
        # prevent caching of the latest file
        headers = {'Cache-Control': 'max-age=0'}
        latest_obj.set_contents_from_string(manifest_contents, headers=headers)
        latest_obj.set_acl('public-read')
        print 'PUT ' + latest_key

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--notes', metavar='NOTES', required=True,
                        help='path to very brief release notes')
    parser.add_argument('--bundle', metavar='BUNDLE_PATH', required=True,
                        help='path to the bundle')
    parser.add_argument('--layouts', metavar='LAYOUTS_PATH', required=True,
                        help='path to the layouts json')
    stage_choices = [
        'release', 'test', 'release-v2',
        'release-v3', 'beta', 'nightly',
        'porksmoothie', 'garlicsoda', 'anchovietea'
        ]
    parser.add_argument('--stage', metavar='STAGE', choices=stage_choices,
                        required=True, help='the target release stage')
    parser.add_argument("--latest", action="store_true",
                        help="update the release stage's 'latest.json' manifest")
    parser.add_argument("-d", "--dry_run", action="store_true", help="don't modify s3")

    args = parser.parse_args()

    print args

    print 'HI'
    push_to_s3(bundle_path=args.bundle,
               layouts_path=args.layouts,
               stage=args.stage,
               notes=args.notes,
               update_latest=args.latest,
               dry_run=args.dry_run)
    print 'BYE'

if (__name__ == '__main__'):
    main()