/**
   Post-bootstrap initialization code for s2's fslcgi. Must live in the
   root of "resource directory", but that (with sufficient hacking) may
   live outside of the web-root.
*/
affirm 'undefined' !== typename $CGI /* expecting CGI module to be loaded under this name */;
affirm Fossil.require;


$CGI.config = {
    resourceDir: Fossil.file.canonicalName(__FILEDIR,true),
    cgiRoot: '<TODO:cgiRoot>'
};
scope {
    const C = $CGI;
    var uri = s2.getenv('SCRIPT_NAME');
    // Set up cgiRoot (the "standard" way of getting the root dir
    // for link-building purposes). This kludgery is not generic...
    if(uri) { C.config.cgiRoot = uri + '/' }
    else{
        uri = s2.getenv('REQUEST_URI');
        C.request.ENV || (C.request.REQUEST_URI = uri);
        if(uri){
            var head = uri.split('fslcgi',2).0;//, head = adj.0;
            C.config.cgiRoot = head ||| '/empty';
            //('/' === head) ? head : head + '/'; //.concat('fslcgi', '/');
        }
    }
    
    C.config.localServerMode =
        (var serverName = s2.getenv('SERVER_NAME'))
        && (0<serverName.indexOf(".local"));
    ;;
}

/**
   A convenience alias for s2.io.output (or functionally
   equivalent).
*/
$CGI.out = s2.io.output;

/**
   Sends its RHS argument output to the (buffered) response body.
   Returns this object (so it can be chained).
*/
$CGI.'operator<<' = proc(self,arg){
    return s2.io.output(arg), this;
};

/**
    Removes "potentially security-relevant" properties from the
    exception ex. Returns ex.
*/
$CGI.scrubException = proc(ex){
    if(this.config.scrubExceptions){
        unset ex.script, ex.stackTrace, ex.line, ex.column // security-relevant
    }
    return ex;
};

$CGI.util = {
    /**
       Converts all key/value pairs from obj to a string of urlencoded
       key value pairs, separated by '&'. If addQMark is true then the
       result is prefixed by a '?', otherwise it is not.

       If obj has no properties then an empty string is returned,
       regardless of addQMark.

       Property key order in the result string is unspecified.
    */
    objToUrlOpt: proc(obj, addQMark = false){
        affirm obj && obj.eachProperty /* expecting an Object */;
        buf.reset();
        var counter = 0;
        obj.eachProperty(each);
        return buf.isEmpty()
            ? ''
            : (addQMark ? '?' : '')+buf.toString();
    }.importSymbols({
        buf: s2.Buffer.new(),
        each: proc(k,v){
            counter++ && buf.append('&');
            buf.append($CGI.urlencode(''+k),'=',$CGI.urlencode(''+v));
            /* Reminder to self: $CGI.urlencode() and friends require
               that $CGI be their 'this' because they reuse an internal
               buffer on each call to save on allocations.
            */
        }
    }),

    absoluteLink: proc(path,label=path, urlOpt){
        affirm 'string' === typename path;
        const hasQ = path.indexOf('?')>=0,
          optStr = this.objToUrlOpt(urlOpt, !hasQ );
        return fmt.applyFormat(
            $CGI.config.cgiRoot,
            $CGI.urlencode(path)+(optStr ? hasQ ? '&' + optStr : optStr : ''),
            label);
    }.importSymbols({
        fmt: "<a href='%1$s%2$s'>%3$s</a>"
    });
};


$CGI.resourcePath = proc(name){
    return this.config.resourceDir + name;
};

$CGI.resolveResource = proc(name){
    const path = this.config.resourceDir + name;
    return Fossil.file.isFile(path) ? path : undefined;
};

// incomplete... not yet sure what i want.
$CGI.request.pathInfoList = scope {
    var ps, p = s2.getenv('PATH_INFO');
    if(p && '/'!==p){
        ps = p.split('/');
        // Trim leading/trailing null entries caused
        // by leading/trailing slashes:
        ps.0 || ps.shift();
        ps[ps.length()-1] || ps.pop();
    }
    ps;
};

if(0){ // only for testing
    $CGI.setContentType('application/json');
    print({request: $CGI.request,
          config: $CGI.config}.toJSONString(2));
}else if(1){
    const c = $CGI;
    c.setContentType('text/plain');
    print("$CGI sanity checks...");
    c << 'Fossil.time.cpuTime() says: '
        << Fossil.time.cpuTime()
        << '\n'
    ;

    const f = c.fossilInstance;
    f.db || f.openCheckout();
    c << 'Fossil context: ' << f << ' repo db: ' << f.db.repo.filename << '\n';
    c << "pathInfoList = "<<c.request.pathInfoList<<'\n';
    c << "resourcePath(foo/bar.baz)==>"
        << c.resourcePath('foo/bar.baz')
        << '\n';
    c << "resolveResource('init.s2')==>"
        << c.resolveResource('init.s2')
        << '\n';
    c << "resolveResource('x')==>"
        << c.resolveResource('x')
        << '\n';

    if(1) {
        c << '$CGI.config = ' << $CGI.config.toJSONString(2) << '\n';
    }

    c << "Most recent timeline event: "
        << f.db.selectRow('SELECT * FROM event ORDER BY mtime DESC')
        .toJSONString(2)
        << '\n';

    if(0){
        // Workaround: c is a Native, and those are rejected
        // by the C-level toJSON bits:
        var obj = {};
        c.eachProperty(proc(k,v){obj[k]=v});
        c << '$CGI properties:\n' << obj.toJSONString(4) << '\n';
    }
    if(1){
        c << 'objToUrlOpt: '
            << c.util.objToUrlOpt({a:3,c:'a b'})
            << '\n';
        c << 'objToUrlOpt again (should be empty): '
            << c.util.objToUrlOpt({},true)
            << '\n';
        c << 'absoluteLink: '
            << c.util.absoluteLink('foo/bar/baz?x=y', 'link', {a:1,b:'hi !'})
            << '\n';
        c << 'absoluteLink again: '
            << c.util.absoluteLink('foo/bar/', 'link', {a:1,b:'hi !'})
            << '\n';
        c << 'Fossil.artifactTypes = '<<Fossil.artifactTypes<<'\n';
        
    }

    0 && Fossil.require(['tmpl-compiled!layout/default.tmpl.s2'],proc(t){
        t.evalContents('default layout');
    });

    c << '\nThe end. Fossil.time.cpuTime() says: '
        << Fossil.time.cpuTime()
        << '\n';

    //throw exception(-666,"testing discarding of buffered output");
}
