/*
 * HtmlGenerator.java
 *
 Copyright (C) 2008 fwd
 
   This file is part of I2PSnarkXL.
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package org.i2p.i2psnarkxl.template.web;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.text.*;

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import javax.imageio.ImageIO;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;

import org.i2p.i2psnarkxl.template.util.conf.TemplateConfig;
import org.i2p.i2psnarkxl.peermanager.utils.BTPeerIDByteDecoder;
import net.i2p.data.Destination;
import net.i2p.data.Base64;
import net.i2p.I2PAppContext;
import org.klomp.snarkxl.BitField;
import org.klomp.snarkxl.I2PSnarkUtil;
import org.klomp.snarkxl.Peer;
import org.klomp.snarkxl.PeerID;
import org.klomp.snarkxl.Snark;
import org.klomp.snarkxl.SnarkManager;
import org.klomp.snarkxl.TrackerClient;

import org.i2p.i2psnarkxl.Client; // XL
import org.i2p.i2psnarkxl.BitfieldImage; // XL

import net.i2p.util.Log;

public class HtmlGenerator extends Thread implements Runnable{
    private static HtmlGenerator _instance = new HtmlGenerator();
    public static HtmlGenerator instance() { return _instance; }
    
    private static final TemplateConfig _templateConfig = TemplateConfig.instance();
    
    private I2PAppContext _context;
    private Log _log;
    private SnarkManager _manager;
    
    private static final String TEMPLATE_PATH = "templates/i2psnarkxl/";
    
    // 1
    private static final String HTML_TEMPLATE = "template_html.html";
    private static final String HTML_CLIENTS_TEMPLATE = "clients/template_html_clients.html";
    private static final String HTML_CONFIGS_TEMPLATE = "configs/template_html_configs.html";
    
    // 2
    private static final String TORRENT_TEMPLATE = "template_torrent.html";
    private static final String CLIENTS_TEMPLATE = "clients/template_clients.html";
    private static final String CONFIGS_TEMPLATE = "configs/template_configs.html";
    // 3
    private static final String TORRENT_ITERATOR = "iterator_torrents_template.html";
    private static final String CLIENT_ITERATOR = "clients/iterator_clients_template.html";
    
    // 4
    private static final String PEERS_TEMPLATE = "template_torrent_peers.html";
    private static final String CLIENT_INFO_TEMPLATE = "clients/template_client_info.html";
    
    // 5
    private static final String TORRENT_PEER_ITERATOR = "iterator_torrents_peer_template.html";
    private static final String CLIENT_TORRENTS_ITERATOR = "clients/iterator_clients_torrents_template.html";
    
    private static final String TEMPLATE_BASE = "/themes/i2psnarkxl/";
    
    // removed private static final int I2PSNARK = 1;
    // removed private static final int I2PSNARKXL = 2;
    // removed private static final int I2PRUFUS = 3;
    // removed private static final int I2P_BT = 4;
    // removed private static final int AZUREUS = 5;
    // removed private static final int ROBERT = 6;
    // superseed
    boolean superseedchange = false;
    Snark superseedsnark = null;
    
    private boolean showPeers = false;
    private boolean showClients = false;
    private String page = "main";
    private boolean useUserConfig = false;
    private static String userConfigFile = null;
    private static String charset = null;
    private static String defaultCharset = "UTF-8";
    String my_id = null;
    /** Creates a new instance of HtmlGenerator */
    private HtmlGenerator() {
        _context = I2PAppContext.getGlobalContext();
        _log = _context.logManager().getLog(HtmlGenerator.class);
    }
    
   /* public HtmlGenerator(boolean test) {
    
    }
    */
    //get content of templates
    private final StringBuffer getContent(String theme, String filename){
        StringBuffer content = null;
        File file = new File(TEMPLATE_PATH + theme + "/" + filename);
        //File[] filelist = dir.listFiles();
        if(file.exists()){
            FileReader fin = null;
            try {
                fin = new FileReader(file);
                BufferedReader in = new BufferedReader(fin);
                String temp;
                while ((temp = in.readLine()) != null) {
                    if(content == null){
                        content = new StringBuffer();
                    }
                    if(content != null && !temp.startsWith("##")){
                        content.append(temp + "\n");
                    }
                }
                fin.close();
            } catch (java.io.IOException ioe) {
                _log.warn( "file "+ file.getPath() + "not found", ioe);
                return null;
            }
        }
        return content;
    }
    
    private void display(String theme, HttpServletRequest req, HttpServletResponse resp){
        //get content of theme-template-main-file - 'html_template.html'
        String maintemplate;
        if(page.equals("clients")){
            maintemplate = HTML_CLIENTS_TEMPLATE;
        }else if(page.equals("configs")){
            maintemplate = HTML_CONFIGS_TEMPLATE;
        }else {
            //page.equals("main") // default
            maintemplate = HTML_TEMPLATE;
        }
        
        StringBuffer template_html = getContent(theme, maintemplate);
        if(template_html == null){
            try{
                fireDisplayError(theme, maintemplate, req, resp);
            } catch (IOException ioe) {
                _log.warn("hrm: " + ioe);
                return;
            }
        }else{
            
            // cleanup last settings
            superseedchange = false;
            superseedsnark = null;
            _templateConfig.resetDefaultConfig(); // to load possible new settings
            _templateConfig.resetUserConfig();
            charset = null;
            useUserConfig = existsUserCongig(theme);
            my_id = null;
            //key_Charset
            charset = _templateConfig.getDefaultProberty( "key_Charset",TEMPLATE_PATH + theme );
            if(charset == null ){
                charset = defaultCharset;
            }else{
                if(! _templateConfig.finalCharset.contains(charset) || charset.trim().equals("")){//equals("") should never happens but ... murpy said ..
                    charset = defaultCharset;
                }else{
                    //? anything forgotten?
                }
            }
            // get new settings
            if(useUserConfig){
                // if trim() causes a nullpointerexception here than some really wired happens because "existsUserCongig(theme)" checked for nullpointer problems before
                userConfigFile = _templateConfig.getDefaultProberty("key_UserConfig",TEMPLATE_PATH + theme).trim();
            }
            //first of all replace the user settings!
            if(useUserConfig){
                replaceAllUserKeys(template_html,theme);
            }
            //replace all keywords
            doMessages(template_html);
            if(page.equals("clients")){
                doClients(theme, template_html, req, resp);
            }else if(page.equals("configs")){
                doConfigs(theme, template_html, req, resp);
            }else{
                doTorrents(theme, template_html, req, resp);
            }
            
            // this should be at least since we use user keywords
            doTitle(template_html);
            doVersionXL(template_html);
            doCharset(template_html);//charset
            doMyPeer(template_html);//my_id
            
            doClientsXL(template_html);//charset
            //toDo: make a method to replace all general keywords, so we save here some lines (keep it small!)
            //replace all theme_resource
            replaceAll(template_html, "theme_resource/", ""+ TEMPLATE_BASE + theme + "/");
            // show peers ; the template have to care about prefix '?' or '&' NOT we! (req|resp)
            if(showPeers){//toDo: if we just deliver true or false (or a int for commons issues) instead "p=" we could use this value even for some other stuff in the templates that shown us the current state of showpeers (like toogle a image; switch style, class ... etc.)
                replaceAll(template_html, "{link_ext_showpeers}", "p=1");
            }else{
                replaceAll(template_html, "{link_ext_showpeers}", "p=0");
            }
            if(showClients){//toDo: if we just deliver true or false (or a int for commons issues) instead "p=" we could use this value even for some other stuff in the templates that shown us the current state of showpeers (like toogle a image; switch style, class ... etc.)
                replaceAll(template_html, "{link_ext_showclients}", "c=1");
            }else{
                replaceAll(template_html, "{link_ext_showclients}", "c=0");
            }
            if(req.getParameter("clients") != null)
                replaceAll(template_html, "{link_ext_clients}", "clients=" + req.getParameter("clients"));
            //write out
            try{
                fireContent(template_html, req, resp);
            } catch (IOException ioe) {
                _log.warn("hrm: " + ioe);
            }
            
            //+++++++++++++++++++++++++++++++++++++++++++++++++
            if(superseedchange && superseedsnark != null){
                superseedsnark.stopTorrent();
                
                try {
                    //Random randB = new Random(System.currentTimeMillis());//fwd save CPU power
                    //Thread.sleep(value + (Math.abs(randB.nextInt()) % 4000));
                    Thread.sleep(5000);//sleep 5 sec
                } catch (InterruptedException e2) {
                    e2.printStackTrace();
                }
                
                superseedsnark.startTorrent();
                
            }
            //++++++++++++++++++++++++++++++++++++++++++++++++++++++
        }
        /*
        try{
            fireDisplaySuccess( req, resp);
        } catch (IOException ioe) {
            _log.warn("hrm: " + ioe);
        }*/
    }
    
    public synchronized void showConfigsPage(String theme, HttpServletRequest req, HttpServletResponse resp) {
        if(theme != null){
            page = "configs";
            //toDo: make a init methode and move this in
            //try p= c=
            
            display(theme, req, resp);
        }else{
            ArrayList themes = getThemes();
            if(themes != null && themes.size() > 0){
                display((String)(themes.get(0)), req, resp);
            }else{
                try{
                    fireDisplayError(theme, "", req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }
        }
    }
    
    public synchronized void showClientsPage(String theme, HttpServletRequest req, HttpServletResponse resp) {
        if(theme != null){
            page = "clients";
            //toDo: make a init methode and move this in
            try{
                String peerParam = req.getParameter("c");
                if (peerParam != null && Integer.valueOf(peerParam) > 0) {
                    this.showClients = true;
                } else {
                    this.showClients = false;
                }
            }catch(NumberFormatException nfe) {
                this.showClients = false;
            }
            
            //toDo: make a init methode and move this in
            String toogleClients = req.getParameter("tc");
            try{
                if (toogleClients != null && Integer.valueOf(toogleClients) > 0) {
                    if(showClients){
                        this.showClients = false;
                    }else{
                        this.showClients = true;
                    }
                }
            }catch(NumberFormatException nfe) {
                this.showClients = false;
            }
            
            display(theme, req, resp);
        }else{
            ArrayList themes = getThemes();
            if(themes != null && themes.size() > 0){
                display((String)(themes.get(0)), req, resp);
            }else{
                try{
                    fireDisplayError(theme, "", req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }
        }
    }
    
    public synchronized void showTheme(String theme, HttpServletRequest req, HttpServletResponse resp) {
        if(theme != null){
            page = "main";
            //toDo: make a init methode and move this in
            try{
                String peerParam = req.getParameter("p");
                if (peerParam != null && Integer.valueOf(peerParam) > 0) {
                    this.showPeers = true;
                } else {
                    this.showPeers = false;
                }
            }catch(NumberFormatException nfe) {
                this.showPeers = false;
            }
            //toDo: make a init methode and move this in
            String tooglePeers = req.getParameter("tp");
            try{
                if (tooglePeers != null && Integer.valueOf(tooglePeers) > 0) {
                    if(showPeers){
                        this.showPeers = false;
                    }else{
                        this.showPeers = true;
                    }
                }
            }catch(NumberFormatException nfe) {
                this.showPeers = false;
            }
            
            display(theme, req, resp);
        }else{
            ArrayList themes = getThemes();
            if(themes != null && themes.size() > 0){
                display((String)(themes.get(0)), req, resp);
            }else{
                try{
                    fireDisplayError(theme, "", req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }
        }
    }
    
    //toDo: remove this after testing // NO, not removing, enhance it! first nOOb was catched by a wrong installation! and this was very useful!
    private void fireDisplayError(String theme, String file, HttpServletRequest req, HttpServletResponse resp) throws IOException{
        // 404
        String ERROR_404 = "<html>\n" +
                "<head>\n" +
                "<title>" + "404" + "</title>\n" +
                "</head>\n" +
                "<body>Theme '" + theme + "/" + file + "' not found or empty!\n" +
                "</body>\n" +
                "</html>\n";
        PrintWriter out = resp.getWriter();
        out.write(ERROR_404);
        return;
    }
    
    //toDo: remove this after testing
    private void fireDisplaySuccess(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        // 404
        String SUCCESS_404 = "<html>\n" +
                "<head>\n" +
                "<title>" + "success" + "</title>\n" +
                "</head>\n" +
                "<body>success!\n" +
                "</body>\n" +
                "</html>\n";
        PrintWriter out = resp.getWriter();
        out.write(SUCCESS_404);
        return;
    }
    
    private void fireContent(StringBuffer content, HttpServletRequest req, HttpServletResponse resp) throws IOException{
        if(charset == null){
            charset = defaultCharset;
        }
        resp.setCharacterEncoding(charset);
        resp.setContentType("text/html; charset=" + charset);
        PrintWriter out = resp.getWriter();
        out.write(content.toString());
        return;
    }
    
    private synchronized void fireImage(String id, HttpServletRequest req, HttpServletResponse resp) throws IOException{
        if(charset == null){
            charset = defaultCharset;
        }
        int width = 200;
        int snarknumber = -1;
        BufferedImage bufferedImage = getImageBarNull( 1,  1);
        String pidS = req.getParameter("pid");
        String widthS = req.getParameter("width");
        String snarkS = req.getParameter("snark");
        try{
            if (widthS != null && snarkS != null) {
                width = Integer.valueOf(widthS);
                snarknumber = Integer.valueOf(snarkS);
            }else{
                _log.warn("HttpServletRequest fireImage nullpointer: pid: " + pidS + " width: " + widthS + " snark: " + snarkS );
                //getImageBarNull( 1,  1);
                //return;
            }
        }catch(NumberFormatException nfe) {
            _log.warn("HttpServletRequest fireImage: pid: " + pidS + " width: " + widthS + " snark: " + snarkS + " ERROR: " + nfe);
            //getImageBarNull( 1,  1);
            //return;
        }
        java.util.List snarks = getSortedSnarks();
        Snark snark = (Snark)snarks.get(snarknumber);
        Peer cur = null;
        if (snark.coordinator != null) {
            java.util.List peers = snark.coordinator.peerList();
            synchronized(peers) {
                Iterator it = peers.iterator();
                while (it.hasNext()) {
                    //PEER_ITERATOR
                    Peer peer = (Peer)it.next();
                    
                    if (!peer.isConnected()){
                        continue;
                    }
                    if(peer.getPeerID().toString().equals(pidS) ){
                        cur = peer;
                        break;
                    }
                }
            }
        }
        
        Client client = null;
        //synchronized(Client.instance().clientsXL) {
        client = Client.instance().getClientByPeer(cur);
        //}
        
        resp.setCharacterEncoding(charset);
        resp.setContentType("image/png");
        resp.setHeader("Cache-Control", "no-store");
        resp.setHeader("Pragma", "no-cache");
        resp.setDateHeader("Expires", 0);
        
        
        
        
        ServletOutputStream responseOutputStream = resp.getOutputStream();
        if(client != null && client.getImageBarByte() != null){
            responseOutputStream.write( client.getImageBarByte());
            //bufferedImage = client.getImageBarImage();
        }else{
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
            byte[] imageData = byteArrayOutputStream.toByteArray();
            imageData = byteArrayOutputStream.toByteArray();
            responseOutputStream.write(imageData);
        }
        responseOutputStream.flush();
        responseOutputStream.close();
        //PrintWriter out = resp.getWriter();
        //out.write(content.toString());
        return;
    }
    
    public synchronized ArrayList getThemes() {
        File dir = new File(TEMPLATE_PATH);
        File[] filelist = dir.listFiles();
        if(filelist != null && filelist.length > 0){
            ArrayList themes = new ArrayList();
            for (int i=0;i<filelist.length; i++) {
                File current = filelist[i];
                if(current.isDirectory()){
                    themes.add(current.getName());
                }
            }
            if(!themes.isEmpty()){
                return themes;
            }
        }
        return null;
    }
    
    private java.util.List getSortedSnarks() {
        Set files = _manager.listTorrentFiles();
        TreeSet fileNames = new TreeSet(files); // sorts it alphabetically
        ArrayList rv = new ArrayList(fileNames.size());
        for (Iterator iter = fileNames.iterator(); iter.hasNext(); ) {
            String name = (String)iter.next();
            Snark snark = _manager.getTorrent(name);
            if (snark != null)
                rv.add(snark);
        }
        return rv;
    }
    
    //    getSortedPeers
    /*
     * toDo: fix the posibilty of lossing a peer in the list by hashmap see class Client sortByName(boolean)
     */
    private java.util.List getSortedPeers(java.util.List peers) {
        //Object[] arrTemp = peersTmp.toArray();
        //Arrays.sort(arrTemp);
        Map peersTmp = new HashMap();
        for(int ai = 0;ai < peers.size();ai++){
            peersTmp.put(peers.get(ai).toString().substring(5, 9).toLowerCase()+ai,peers.get(ai));// + ai because else we lost clients with same name =name1;name5;name34 ..
        }
        //peers.addAll(arrTemp);
        //Set peersSet = _manager.listTorrentFiles();
        Set peersSet =  peersTmp.keySet();
        TreeSet peerTS = new TreeSet(peersSet); // sorts it alphabetically by Hash (current not the name of peers but enough that the peers keep the same range)
        ArrayList rv = new ArrayList(peerTS.size());
        for (Iterator iter = peerTS.iterator(); iter.hasNext(); ) {
            String name = (String )iter.next();
            rv.add((Peer)(peersTmp.get(name)));
            //rv.add(peers.get(peers.indexOf(peer)));
        }
        return rv;
    }
    
    //keyword {messages}
    private void doMessages(StringBuffer content){
        if(content.indexOf("{messages}") != -1){
            StringBuffer messages = new StringBuffer();
            java.util.List msgs = _manager.getMessages();
            for (int i = msgs.size()-1; i >= 0; i--) {
                String msg = (String)msgs.get(i);
                messages.append(msg + "<br>");
            }
            replaceAll(content, "{messages}", ""+ messages.toString());
        }
    }
    
    // keyword versionXL
    
    private void doVersionXL(StringBuffer content){
        replaceAll(content, "{versionXL}", ""+ _manager.getVersionXL());
    }
    
    //keyword {title}
    private void doTitle(StringBuffer content){
        replaceAll(content, "{title}", ""+ _manager.getNickname());
    }
    
    //keyword {charset}
    private void doCharset(StringBuffer content){
        replaceAll(content, "{charset}", charset);
    }
    
    //keyword {clientsXL}
    private void doClientsXL(StringBuffer content){
        StringBuffer output = new StringBuffer("");
        for(int i = 0; i < Client.instance().clientsXL.size(); i++){
            output.append("" + i + " " + ((Client)(Client.instance().clientsXL.get(i))).toString());
            output.append("<br>");
        }
        output.append("" + Client.instance().clientsXL.size());
        replaceAll(content, "{clientsXL}", output.toString());
    }
    
    //keyword {my_id}
    private void doMyPeer(StringBuffer content){
        if(my_id != null){
            replaceAll(content, "{my_peer_id}", my_id);
        }else{
            replaceAll(content, "{my_peer_id}", "");
        }
    }
    // replaceAll(bufTorrents, "{my_peer}", "" + my_id);
    //keyword {torrent_template} toDo: after testphase cleanup and make a few small methods of this monster
    private void doTorrents(String theme, StringBuffer content, HttpServletRequest req, HttpServletResponse resp){
        if(content.indexOf("{torrent_template}") != -1){
            StringBuffer torrent_template = getContent(theme, TORRENT_TEMPLATE);
            if(torrent_template == null){
                try{
                    fireDisplayError(theme, TORRENT_TEMPLATE, req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }else{
                //first of all replace the user settings!
                if(useUserConfig){
                    replaceAllUserKeys(torrent_template,theme);
                }
                
                if(torrent_template.indexOf("{iterator_torrents_template}") != -1){
                    StringBuffer iterator_torrents_template = getContent(theme, TORRENT_ITERATOR);
                    if(iterator_torrents_template != null){
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(iterator_torrents_template,theme);
                        }
                        StringBuffer template_torrent_peers = getContent(theme, PEERS_TEMPLATE); //preload (needed in peer--iterator
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(template_torrent_peers,theme);
                        }
                        StringBuffer iterator_torrents_peer_template = getContent(theme, TORRENT_PEER_ITERATOR); //preload (needed in peer--iterator
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(iterator_torrents_peer_template,theme);
                        }
                        StringBuffer bufTorrents = new StringBuffer();
                        java.util.List snarks = getSortedSnarks();
                        //totals
                        long downBpsTotal = 0;
                        long upBpsTotal  = 0;
                        long uploadedTotal = 0;
                        long downloadedTotal = 0;
                        
                        //load defaults toDo make a extern methode
                        //current the "default.config" is not as tempfile in memory ; toDo after the testphase make a memory-managment for the config-files .. (?md5 check for changes?)
                        String keyProgressBarPiece = _templateConfig.getDefaultProberty( "key_ProgressBar_Piece",TEMPLATE_PATH + theme );
                        String keyProgressBarNoPiece = _templateConfig.getDefaultProberty( "key_ProgressBar_NoPiece",TEMPLATE_PATH + theme );
                        String keyProgressBarReqPiece = _templateConfig.getDefaultProberty( "key_ProgressBar_ReqPiece",TEMPLATE_PATH + theme );
                        
                        
                        
                        if(keyProgressBarPiece == null){
                            keyProgressBarPiece = "|";
                        }
                        
                        if(keyProgressBarReqPiece == null){
                            keyProgressBarPiece = ":";
                        }
                        
                        if(keyProgressBarNoPiece == null){
                            keyProgressBarPiece = "&nbsp";
                        }
                        
                        //key_ProgressBar_MaxPieces
                        String tempKeyProgressBarMaxPieces = _templateConfig.getDefaultProberty( "key_ProgressBar_MaxPieces",TEMPLATE_PATH + theme );
                        int keyProgressBarMaxPieces = -1;
                        if(tempKeyProgressBarMaxPieces != null){
                            try{
                                keyProgressBarMaxPieces =  Integer.valueOf(tempKeyProgressBarMaxPieces);
                            }catch(NumberFormatException nfe) {
                                _log.warn("nfe: tempKeyProgressBarMaxPieces in " +TEMPLATE_PATH + theme + " \"default.config\" contains no values! :" + nfe);
                                //return;
                            }
                        }
                        
                        if(keyProgressBarMaxPieces < -1){
                            keyProgressBarMaxPieces = -1;
                        }
                        
                        //key_torrent_ProgressBar_MaxPieces
                        String tempKeyTorrentProgressBarMaxPieces = _templateConfig.getDefaultProberty( "key_torrent_ProgressBar_MaxPieces",TEMPLATE_PATH + theme );
                        int keyTorrentProgressBarMaxPieces = -1;
                        if(tempKeyTorrentProgressBarMaxPieces != null){
                            try{
                                keyTorrentProgressBarMaxPieces =  Integer.valueOf(tempKeyTorrentProgressBarMaxPieces);
                            }catch(NumberFormatException nfe) {
                                _log.warn("nfe: tempKeyTorrentProgressBarMaxPieces in " +TEMPLATE_PATH + theme + " \"default.config\" contains no values! :" + nfe);
                                //return;
                            }
                        }
                        
                        if(keyTorrentProgressBarMaxPieces < -1){
                            keyTorrentProgressBarMaxPieces = -1;
                        }
                        
                        
                        for (int i = 0; i < snarks.size(); i++) {
                            int snarknumber = i;
                            //TORRENT_ITERATOR
                            String crap = "|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||";
                            
                            Snark snark = (Snark)snarks.get(i);
                            boolean isComplete = false;
                            File torrent = new File(snark.torrent);
                            String filename = torrent.getName();
                            filename = filename.substring(0, filename.lastIndexOf(".torrent"));
                            bufTorrents.append(new String(iterator_torrents_template.toString()));
                            replaceAll(bufTorrents, "{torrent_name}", filename);
                            
                            replaceAll(bufTorrents, "{torrent_InfoHash}", "" + Base64.encode(snark.meta.getInfoHash()));
                            replaceAll(bufTorrents, "{torrent_InfoHash_urlencoded}", "" + TrackerClient.urlencode(snark.meta.getInfoHash()));
                            //TrackerClient.urlencode(snark.meta.getInfoHash())
                            //"?p=" + Base64.encode(snark.meta.getInfoHash())
                            
                            // filesize
                            long total = snark.meta.getTotalLength();
                            
                            //fwd snark.storage.needed() is a wrong named opposite aquivalent of new method snark.coordinator.getCompleted() method ... just to remember for myself
                            // i really need to become more familary with the source! "Viele Wege führen nach Rom!" ;)
                            // Early typecast, avoid possibly overflowing a temp integer
                            long remaining = (long) snark.storage.needed() * (long) snark.meta.getPieceLength(0);
                            if (remaining > total){
                                remaining = total;
                            }
                            
                            //torrent percent toDo: make getMethode
                            float tpct = (float) 100.0 - (float) (100.0 * (float) remaining / total  );// could this give a division by zero exception?
                            if(tpct < 0){tpct = (float) 0.0;} //
                            String tps = String.valueOf(tpct);
                            if (tps.length() > 5)
                                tps = tps.substring(0, 5);
                            replaceAll(bufTorrents, "{torrent_percent}", "" + tps);
                            
                            //progressBar_size (minimalistic style to strech some width="tpbSize")
                            if(bufTorrents.indexOf("{torrent_progressbar_size}") != -1){
                                if(tpct >= 1){
                                    int tpbSize = (int)((100f / 100f) * tpct);
                                    replaceAll(bufTorrents, "{torrent_progressbar_size}", "" + tpbSize);
                                }else{
                                    replaceAll(bufTorrents, "{torrent_progressbar_size}", "1");
                                }
                            }
                            //snark.storage.putPiece();
                            replaceAll(bufTorrents, "{torrent_dlsize}", formatSize(total-remaining));
                            replaceAll(bufTorrents, "{torrent_size}", formatSize(total));
                            
                            boolean isOurOwnAvailablePiecesOfIncompletedTorrentAdded =false;
                            if(!(tpct < 100)){// torrent is completed. do not add it to the stats of available pieces
                                isOurOwnAvailablePiecesOfIncompletedTorrentAdded = true;
                            }
                            // moved in this loop without coordinator and use new methods; yet not started torrents shown this infos too
                            //toDo: cleanup previous and now commented (//) after testphase
                            int torrentPiecesCompleted = snark.getCompleted();
                            replaceAll(bufTorrents, "{torrent_pieces_completed}", "" + torrentPiecesCompleted);
                            int pieces = snark.meta.getPieces();
                            replaceAll(bufTorrents, "{torrent_piece_count}", "" + pieces);
                            
                            while(crap.length() < pieces)
                                crap += crap;
                            
                            crap = crap.substring(0,pieces);
                            
                            // this used a temporary added method
                            // toDo: remove the temporary local methode and create a XOR methode in bitfield or storage so we can use the same method for peers and torents to get the availables pieces!
                            // --------------------------------------------
                            ArrayList allTorrentPieces = null;
                            if(torrentPiecesCompleted > 0 && bufTorrents.indexOf("{torrent_pieces_string}") != -1 && tpct < 100){//
                                allTorrentPieces = getAllPieces(snark.storage.getBitField());//see: peer.getAllPieces(); temporary local added method
                                
                                StringBuffer torrentPiecesBar = new StringBuffer();
                                //toDo: for pieces over ??? we need to shorten it
                                //toDo: if allKnownPeerPieces.size() is less counted ... why running complete bitmap.size (could be 5000 or more for each peer); find a better way and save cpu-usages!
                                for (int p = 0; p < pieces; p++) {      // toDO: faster is a XOR and saves resources
                                    if(keyTorrentProgressBarMaxPieces != -1 && p >= keyTorrentProgressBarMaxPieces){
                                        break;
                                    }
                                    if(allTorrentPieces.contains(p)){
                                        
                                        if(keyTorrentProgressBarMaxPieces == -1 || p < keyTorrentProgressBarMaxPieces){
                                            torrentPiecesBar.append(keyProgressBarPiece);
                                        }
                                        
                                    }else{
                                        if(keyTorrentProgressBarMaxPieces == -1 || p < keyTorrentProgressBarMaxPieces){
                                            torrentPiecesBar.append(keyProgressBarNoPiece);
                                        }
                                    }
                                }
                                replaceAll(bufTorrents, "{torrent_pieces_string}", "" + new String(torrentPiecesBar.toString()));
                            }else{
                                if ( tpct >= 100 /*&& peer.isConnected() is asked before too*/){// 100% is a seeder toDo: this is worst find something better or make a static final of it
                                    if(keyTorrentProgressBarMaxPieces < 0 || keyTorrentProgressBarMaxPieces > crap.length())
                                        replaceAll(bufTorrents, "{torrent_pieces_string}", crap);
                                    else
                                        replaceAll(bufTorrents, "{torrent_pieces_string}", crap.substring(0, keyTorrentProgressBarMaxPieces));
                                }else{
                                    replaceAll(bufTorrents, "{torrent_pieces_string}", "-" );
                                }
                            }
                            // --------------------------------------------
                            String peerParam = null;
                            peerParam = req.getParameter("p");
                            if (snark.coordinator != null) {
                                snark.coordinator.cleanupPeers();//seeder/seeder
                                String onlyseedParam = null;
                                onlyseedParam = req.getParameter("onlyseed");
                                if(onlyseedParam != null && onlyseedParam.equals("1") &&  Base64.encode(snark.meta.getInfoHash()).equals(peerParam)){
                                    snark.coordinator.setOnlyseed(true);
                                }else{
                                    if(onlyseedParam != null && onlyseedParam.equals("0") &&  Base64.encode(snark.meta.getInfoHash()).equals(peerParam)){
                                        snark.coordinator.setOnlyseed(false);
                                    }
                                }
                                replaceAll(bufTorrents, "{torrent_onlyseed}", "" + snark.coordinator.isOnlyseed());
                                boolean isRunning = !snark.stopped;
                                int curPeers = snark.coordinator.getPeerCount();
                                //int pieces = snark.meta.getPieces();// moved to previous loop without coordinator
                                //int torrentPiecesCompleted = snark.coordinator.getCompleted(); // after studying further the code, this could be also [torrentPiecesCompleted = total-remaining;] and switch one step on top to previous loop without a needed snark.coordinator .. toDo: find more of this kind!
                                int knownPeers = snark.coordinator.trackerSeenPeers;
                                replaceAll(bufTorrents, "{torrent_downloaded}", "" + formatSize(snark.coordinator.getDownloaded()));
                                downloadedTotal += snark.coordinator.getDownloaded();
                                replaceAll(bufTorrents, "{torrent_uploaded}", "" + formatSize(snark.coordinator.getUploaded()));
                                uploadedTotal += snark.coordinator.getUploaded();
                                replaceAll(bufTorrents, "{torrent_peer_count}", "" + curPeers);
                                //replaceAll(bufTorrents, "{torrent_piece_count}", "" + pieces);// moved to previous loop without coordinator
                                //replaceAll(bufTorrents, "{torrent_pieces_completed}", "" + torrentPiecesCompleted);// moved to previous loop without coordinator
                                replaceAll(bufTorrents, "{torrent_known_peer_count}", "" + knownPeers);
                                
                                //toDo: make a get-methode for this
                                if( isRunning ) {
                                    if(my_id == null){
                                        my_id = getIDString(snark.coordinator.getID());
                                        //replaceAll(bufTorrents, "{my_peer}", "" + my_id);
                                    }
                                    //toDo: observe and check this! is the file really completed yet?!
                                    if(tpct == 100.0){
                                        isComplete = true;
                                    }
                                    if(isComplete){
                                        snark.coordinator.setOnlyseed(false);
                                        if(curPeers > 0){
                                            replaceAll(bufTorrents, "{torrent_status}", "Seeding");
                                        }else{
                                            replaceAll(bufTorrents, "{torrent_status}", "Waiting");
                                        }
                                    }else{
                                        if(curPeers > 0){
                                            if(snark.coordinator.isOnlyseed()){
                                                replaceAll(bufTorrents, "{torrent_status}", "OSLeeching");
                                            }else{
                                                replaceAll(bufTorrents, "{torrent_status}", "Leeching");
                                            }
                                        }else{
                                            if(snark.coordinator.isOnlyseed()){
                                                replaceAll(bufTorrents, "{torrent_status}", "OSWaiting");
                                            }else{
                                                replaceAll(bufTorrents, "{torrent_status}", "Waiting");
                                            }
                                        }
                                    }
                                }else{
                                    replaceAll(bufTorrents, "{torrent_status}", "Stopped");
                                }
                                
                                //toDO: add method for peer_template and remove this below else this become a monster methode
                                //...
                                
                                //--------------------------------
                                //{peer_template}
                                
                                
                                
                                if(template_torrent_peers != null && (showPeers) || Base64.encode(snark.meta.getInfoHash()).equals(peerParam)){
                                    
                                    if( isRunning && (curPeers > 0) || snark.storage.isSuperseed) {
                                        replaceAll(bufTorrents, "{template_torrent_peers}", new String(template_torrent_peers.toString()));
                                        
                                        //StringBuffer template_torrent_peers = getContent(theme, PEERS_TEMPLATE);
                                        if(template_torrent_peers != null && iterator_torrents_peer_template != null){
                                            StringBuffer bufPeers = new StringBuffer();
                                            java.util.List peers = snark.coordinator.peerList();
                                            peers = getSortedPeers(peers);
                                            
                                            //totals
                                            int peersTotalRequestedPieces = 0;
                                            long peersTotalUploadRate = 0;
                                            long peersTotalDownloadRate = 0;
                                            long peersTotalUpload = 0;
                                            long peersTotalDownload = 0;
                                            ArrayList allAvailablePeersPieces = null;
                                            //ArrayList allAvailablePeersPieces = new ArrayList();// toDo:
                                            if(!isOurOwnAvailablePiecesOfIncompletedTorrentAdded && allTorrentPieces != null){// now including our own pieces too (if torrent is incompleted
                                                allAvailablePeersPieces = new ArrayList(allTorrentPieces);
                                                isOurOwnAvailablePiecesOfIncompletedTorrentAdded = true;
                                            }else{
                                                if(allAvailablePeersPieces == null){
                                                    allAvailablePeersPieces = new ArrayList();
                                                }
                                            }
                                            ArrayList allKnownPeerPieces = null;
                                            synchronized(peers) {
                                                Iterator it = peers.iterator();
                                                while (it.hasNext()) {
                                                    //PEER_ITERATOR
                                                    Peer peer = (Peer)it.next();
                                                    
                                                    if (!peer.isConnected()){
                                                        continue;
                                                    }
                                                    peer.setOnlyseed(snark.coordinator.isOnlyseed());
                                                    bufPeers.append(new String(iterator_torrents_peer_template.toString()));
                                                    //
                                                    //moved here for 100% peers
                                                    float pct = (float) (100.0 * (float) peer.getCompleted() / snark.meta.getPieces());
                                                    //do not show progressbar pieces for 100% peers (needs a lot of CPU-usage!)
                                                    
                                                    //
                                                    ArrayList arr = null;
                                                    if (!snark.coordinator.halted()){
                                                        arr = getRequestedPieces( peer);
                                                        if(bufPeers.indexOf("{peer_pieces_string}") != -1){
                                                            allKnownPeerPieces = peer.getAllPieces();// toDo: [CPU-usage] ...after testphase do not use this on every intervall less ~2 - 5 minutes. keep this in some temporar (new class) Client value.
                                                        }
                                                        
                                                        if(arr != null){
                                                            replaceAll(bufPeers, "{peer_pieces_requested}", "" + arr.size());
                                                            peersTotalRequestedPieces += arr.size();
                                                        }
                                                    }
                                                    if (!snark.coordinator.halted() && pct < 100 /*&& peer.isConnected() is asked before too*/){//peer.isConnected() returns peer.state != null
                                                        
                                                        
                                                        replaceAll(bufPeers, "{peer_piece_count}", "" + peer.getCompleted());
                                                        StringBuffer piecesBar = new StringBuffer();
                                                        if(allKnownPeerPieces != null){
                                                            if(bufPeers.indexOf("{peer_pieces_string}") != -1){
                                                                //toDo: for pieces over ??? we need to shorten it
                                                                //toDo: if allKnownPeerPieces.size() is less counted ... why running complete bitmap.size (could be 5000 or more for each peer); find a better way and save cpu-usages!
                                                                for (int p = 0; p < peer.getBitmapSize(); p++) {      // toDO: faster is a XOR and saves resources
                                                                    if(allKnownPeerPieces.contains(p)){
                                                                        
                                                                        if(keyProgressBarMaxPieces == -1 || p < keyProgressBarMaxPieces){
                                                                            if(allKnownPeerPieces.contains(p) && arr != null && arr.contains(p)){
                                                                                piecesBar.append(keyProgressBarReqPiece);
                                                                            }else{
                                                                                piecesBar.append(keyProgressBarPiece);
                                                                            }
                                                                        }
                                                                        if(!allAvailablePeersPieces.contains(p)){
                                                                            // current this works only if {peer_pieces_string} is used in the template
                                                                            // toDo: make it available allways to shown it even on peers and/or each torrent
                                                                            allAvailablePeersPieces.add(p);
                                                                        }
                                                                        
                                                                    }else{
                                                                        if(keyProgressBarMaxPieces == -1 || p < keyProgressBarMaxPieces){
                                                                            piecesBar.append(keyProgressBarNoPiece);
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            replaceAll(bufPeers, "{peer_pieces_string}", new String(piecesBar.toString()));
                                                        }
                                                        
                                                        
                                                    }else{
                                                        if (!snark.coordinator.halted() && pct >= 100 /*&& peer.isConnected() is asked before too*/){// 100% is a seeder toDo: this is worst find something better or make a static final of it
                                                            if(keyProgressBarMaxPieces < 0 || keyProgressBarMaxPieces > crap.length())
                                                                replaceAll(bufPeers, "{peer_pieces_string}", crap);
                                                            else
                                                                replaceAll(bufPeers, "{peer_pieces_string}", crap.substring(0, keyProgressBarMaxPieces ));
                                                            
                                                        }
                                                    }
                                                    //clean unused
                                                    replaceAll(bufPeers, "{peer_pieces_string}", "&nbsp;");
                                                    replaceAll(bufPeers, "{peer_pieces_requested}", "");
                                                    replaceAll(bufPeers, "{peer_piece_count}", "");
                                                    //
                                                    //
                                                    
                                                    //name
                                                    replaceAll(bufPeers, "{peer_name}", peer.toString().substring(5, 9));
                                                    replaceAll(bufPeers, "{peer_id}", "" + peer.getPeerID().toString() );
                                                    replaceAll(bufPeers, "{peer_dest}", "" + peer.getPeerID().getAddress().toBase64() );
                                                    replaceAll(bufPeers, "{peer_snarknumber}",  "" + snarknumber );
                                                    //percent
                                                    // moved above because needed for 100% peers: //float pct = (float) (100.0 * (float) peer.getCompleted() / snark.meta.getPieces());
                                                    String ps = String.valueOf(pct);
                                                    if (ps.length() > 5)
                                                        ps = ps.substring(0, 5);
                                                    replaceAll(bufPeers, "{peer_percent}", "" + ps);
                                                    // isInteresting
                                                    replaceAll(bufPeers, "{peer_interesting}", "" + peer.isInteresting());
                                                    // isChoked
                                                    replaceAll(bufPeers, "{peer_choked}", "" + peer.isChoked());
                                                    //dlrate
                                                    replaceAll(bufPeers, "{peer_dlrate}", "" + formatSize(peer.getDownloadRate()) + "ps");
                                                    peersTotalUploadRate += peer.getUploadRate();
                                                    //ulrate
                                                    replaceAll(bufPeers, "{peer_ulrate}", "" + formatSize(peer.getUploadRate()) + "ps");
                                                    peersTotalDownloadRate += peer.getDownloadRate();
                                                    // uploadTotal
                                                    replaceAll(bufPeers, "{peer_ultotal}", "" + formatSize(peer.getUploadTotal()));
                                                    peersTotalUpload += peer.getUploadTotal();
                                                    // downuploadTotal
                                                    replaceAll(bufPeers, "{peer_dltotal}", "" + formatSize(peer.getDownloadTotal()) );
                                                    peersTotalDownload += peer.getDownloadTotal();
                                                    //ratio
                                                    replaceAll(bufPeers, "{peer_hasbadratio}", "" + snark.coordinator.hasBadRatio(peer));
                                                    //progressBar
                                                    if(bufPeers.indexOf("{peer_progressbar}") != -1){
                                                        if(pct >= 0){
                                                            StringBuffer pbar = new StringBuffer();
                                                            int pbSize = (int)((200f / 100f) * pct);
                                                            pbar.append("<img src=\"");
                                                            pbar.append("theme_resource/gbar_left.png\" height=\"16\" width=\"5\" alt=\"\">");
                                                            pbar.append("<img src=\"");
                                                            pbar.append("theme_resource/gbar_middle.png\" height=\"16\" width=\"");
                                                            pbar.append(pbSize);
                                                            pbar.append("\" alt=\"\">");
                                                            pbar.append("<img src=\"");
                                                            pbar.append("theme_resource/bar_middle.png\" height=\"16\" width=\"");
                                                            pbar.append((int)((200f / 100f) *100) - pbSize);
                                                            pbar.append("\" alt=\"\">");
                                                            pbar.append("<img src=\"");
                                                            pbar.append("theme_resource/bar_right.png\" height=\"16\" width=\"5\" alt=\"\">");
                                                        /*if(pct == 100){
                                                            pbar.append("<img src=\"");
                                                            pbar.append("theme_resource/lbar_right.png\" height=\"16\" width=\"5\" alt=\"\">");
                                                        }*/
                                                            replaceAll(bufPeers, "{peer_progressbar}", pbar.toString());
                                                        }else{
                                                            replaceAll(bufPeers, "{peer_progressbar}", "");
                                                        }
                                                        
                                                    }
                                                    //progressBar_size (minimalistic style to strech some widht="pbSize")
                                                    if(bufPeers.indexOf("{peer_progressbar_size}") != -1){
                                                        if(pct >= 1){
                                                            int pbSize = (int)((100f / 100f) * pct);
                                                            replaceAll(bufPeers, "{peer_progressbar_size}", "" + pbSize);
                                                        }else{
                                                            replaceAll(bufPeers, "{peer_progressbar_size}", "1");
                                                        }
                                                    }
                                                    
                                                    //replaceAll(bufPeers, "{peer_userClient}", getUserClient(peer.toString().substring(0, 4)) );
                                                    replaceAll(bufPeers, "{peer_userClient}",  getClientDescription(peer.getPeerID().getID()) );
                                                    
                                                    
                                                }// while (it
                                                
                                            }// synchronize peers
                                            
                                            //superseed
                                            //{peers_superseed_value} {peers_superseed_inverse_value}
                                            // param "supref" "supseed" "suptor"
                                            String supref = req.getParameter("supref");
                                            String supseed = req.getParameter("supseed");
                                            String suptor = req.getParameter("suptor");
                                            String supav = req.getParameter("supav");
                                            
                                            if(suptor != null && suptor.equals(Base64.encode(snark.meta.getInfoHash()))){
                                                boolean superseedpeer = false;
                                                if(supref != null && supref.equals("true")){
                                                    snark.storage.resetBitfieldSuperseed();
                                                    //superseedpeer = true;
                                                }else if(supseed != null && supseed.equals("on")){
                                                    snark.storage.isSuperseed = true;
                                                    superseedpeer = true;
                                                }else if(supseed != null && supseed.equals("off")){
                                                    snark.storage.isSuperseed = false;
                                                    superseedpeer = true;
                                                }else if(curPeers > 0 && supav != null && supav.equals("true")){
                                                    snark.storage.resetBitfieldSuperseedWithAvailablePieces(Base64.encode(snark.meta.getInfoHash()));
                                                    //superseedpeer = true;
                                                }
                                                if(superseedpeer){
                                                    superseedchange = true;
                                                    superseedsnark = snark;
                                                }
                                            }
                                            if(snark.storage.isSuperseed){
                                                replaceAll(bufTorrents, "{peers_superseed_value}", "on");
                                                replaceAll(bufTorrents, "{peers_superseed_inverse_value}", "off");
                                            }else{
                                                replaceAll(bufTorrents, "{peers_superseed_value}", "off");
                                                replaceAll(bufTorrents, "{peers_superseed_inverse_value}", "on");
                                            }
                                            
                                            // totals
                                            // {peers_total_pieces_requested} {peers_total_ulrate} {peers_total_dlrate} {peers_total_ultotal} {peers_total_dltotal}
                                            replaceAll(bufTorrents, "{peers_total_pieces_requested}", "" + peersTotalRequestedPieces);
                                            replaceAll(bufTorrents, "{peers_total_ulrate}", "" + formatSize(peersTotalUploadRate) + "ps");
                                            replaceAll(bufTorrents, "{peers_total_dlrate}", "" + formatSize(peersTotalDownloadRate)+ "ps");
                                            replaceAll(bufTorrents, "{peers_total_ultotal}", "" + formatSize(peersTotalUpload));
                                            replaceAll(bufTorrents, "{peers_total_dltotal}", "" + formatSize(peersTotalDownload));
                                            //allAvailablePeersPieces
                                            //xxx
                                            replaceAll(bufTorrents, "{peers_total_pieces_available}", "" + allAvailablePeersPieces.size());
                                            int piecesByImage = BitfieldImage.instance().getCompletedPiecesByMetainfoHash(Base64.encode(snark.meta.getInfoHash()));
                                            replaceAll(bufTorrents, "{peers_total_pieces_available_by_image}", "" + piecesByImage);
                                            
                                            float piecesAvailableByPeersPercent = (float) (100.0 * (float) allAvailablePeersPieces.size() / (float) pieces  );
                                            String pap = String.valueOf(piecesAvailableByPeersPercent);
                                            if (pap.length() > 5)
                                                pap = pap.substring(0, 5);
                                            replaceAll(bufTorrents, "{peers_total_pieces_available_percent}", pap);
                                            
                                            piecesAvailableByPeersPercent = (float) (100.0 * (float) piecesByImage / (float) pieces  );
                                            pap = String.valueOf(piecesAvailableByPeersPercent);
                                            if (pap.length() > 5)
                                                pap = pap.substring(0, 5);
                                            replaceAll(bufTorrents, "{peers_total_pieces_available_percent_by_image}", pap);
                                            
                                            if(bufTorrents.indexOf("{peers_total_pieces_available_string}") != -1){
                                                //toDo: for pieces over ??? we need to shorten it
                                                //toDo: if allKnownPeerPieces.size() is less counted ... why running complete bitmap.size (could be 5000 or more for each peer); find a better way and save cpu-usages!
                                                if(allAvailablePeersPieces != null && allAvailablePeersPieces.size() < snark.meta.getPieces()){
                                                    StringBuffer piecesBarAvailable = new StringBuffer();
                                                    for (int p = 0; p < snark.meta.getPieces(); p++) {      // toDO: faster is a XOR and saves resources
                                                        if(allAvailablePeersPieces.contains(p)){
                                                            
                                                            if(keyProgressBarMaxPieces == -1 || p < keyProgressBarMaxPieces){
                                                                if(allAvailablePeersPieces.contains(p) /*&& arr != null && arr.contains(p)*/){
                                                                    piecesBarAvailable.append(keyProgressBarPiece);
                                                                }/*else{
                                                                piecesBarAvailable.append(keyProgressBarReqPiece);
                                                            }*/
                                                            }
                                                        }else{
                                                            if(keyProgressBarMaxPieces == -1 || p < keyProgressBarMaxPieces){
                                                                piecesBarAvailable.append(keyProgressBarNoPiece);
                                                            }
                                                        }
                                                    }
                                                    replaceAll(bufTorrents, "{peers_total_pieces_available_string}", new String(piecesBarAvailable.toString()));
                                                }else{// available pieces = 100 % toDo: this is worst find something better or make a static final of it
                                                    if(keyProgressBarMaxPieces < 0 || keyProgressBarMaxPieces > crap.length())
                                                        replaceAll(bufPeers, "{peers_total_pieces_available_string}", crap);
                                                    else
                                                        replaceAll(bufTorrents, "{peers_total_pieces_available_string}", crap.substring(0, keyProgressBarMaxPieces ));
                                                    
                                                    //replaceAll(bufTorrents, "{peers_total_pieces_available_string}", crap.substring(0, keyProgressBarMaxPieces - 1));
                                                    
                                                }
                                            }
                                            //peers_total_progressbar_size (minimalistic style to strech some widht="pbSize")
                                            if(bufTorrents.indexOf("{peers_total_progressbar_size}") != -1){
                                                if(piecesAvailableByPeersPercent >= 1){
                                                    int pbSize = (int)((100f / 100f) * piecesAvailableByPeersPercent);
                                                    replaceAll(bufTorrents, "{peers_total_progressbar_size}", "" + pbSize);
                                                }else{
                                                    replaceAll(bufTorrents, "{peers_total_progressbar_size}", "1");
                                                }
                                            }
                                            replaceAll(bufTorrents, "{peers_torrent_InfoHash}", Base64.encode(snark.meta.getInfoHash()));
                                            // replace the total-peers-sum before we replace {iterator_torrents_peer_template} because maybe some n00b insert the stuff there and wonder of wrong values
                                            
                                            // after all peers
                                            replaceAll(bufTorrents, "{iterator_torrents_peer_template}",  new String(bufPeers.toString()));
                                            bufPeers = null;
                                            //replaceAll(bufTorrents, "{template_torrent_peers}", ""+ bufPeers.toString());
                                        }// template_torrent_peers != null
                                    }// isRunning && curPeers > 0
                                }// template_torrent_peers != null
                                //-------------------------------
                                long downBps = snark.coordinator.getDownloadRate();//toDo: snark.coordinator.getDownloadRate() shows last rate even we have stopped the torrent
                                long upBps = snark.coordinator.getUploadRate();//toDo: snark.coordinator.getUploadRate() shows last rate even we have stopped the torrent
                                if( isRunning && curPeers > 0) {
                                    replaceAll(bufTorrents, "{torrent_ulrate}", "" + formatSize(upBps) + "ps");
                                    replaceAll(bufTorrents, "{torrent_dlrate}", "" + formatSize(downBps) + "ps");
                                    downBpsTotal += downBps;
                                    upBpsTotal += upBps;
                                    
                                    //replaceAll(bufTorrents, "{torrent_link_showpeers}", "p=" + Base64.encode(snark.meta.getInfoHash()));
                                    //"?p=" + Base64.encode(snark.meta.getInfoHash())
                                }
                            }// snark.coordinator != null
                            //cleanup unused but try to give a String back! this could be useful to replace a given picturename in the template ..
                            replaceAll(bufTorrents, "{torrent_status}", "None");
                            replaceAll(bufTorrents, "{torrent_progressbar_size}", "1");
                            replaceAll(bufTorrents, "{torrent_percent}", "-");
                            replaceAll(bufTorrents, "{torrent_piece_count}", "");
                            replaceAll(bufTorrents, "{torrent_pieces_completed}", "");
                            replaceAll(bufTorrents, "{template_torrent_peers}", "");
                            replaceAll(bufTorrents, "{torrent_peer_count}", "-");
                            replaceAll(bufTorrents, "{torrent_known_peer_count}", "-");
                            replaceAll(bufTorrents, "{torrent_downloaded}", "-" );
                            replaceAll(bufTorrents, "{torrent_uploaded}", "-" );
                            replaceAll(bufTorrents, "{torrent_ulrate}", "-" );
                            replaceAll(bufTorrents, "{torrent_dlrate}", "-" );
                            replaceAll(bufTorrents, "{peers_total_pieces_available_string}", "");
                            replaceAll(bufTorrents, "{peers_total_progressbar_size}", "1");
                            replaceAll(bufTorrents, "{torrent_InfoHash}", "");
                            replaceAll(bufTorrents, "{torrent_InfoHash_urlencoded}", "");
                            replaceAll(bufTorrents, "{torrent_onlyseed}", "-");
                            //replaceAll(bufTorrents, "{torrent_link_showpeers}", "" );
                        }// for (i=0 ..
                        replaceAll(torrent_template, "{torrents_ulrate_total}", "" + formatSize(upBpsTotal) + "ps");
                        replaceAll(torrent_template, "{torrents_dlrate_total}", "" + formatSize(downBpsTotal) + "ps");
                        replaceAll(torrent_template, "{torrents_total_uploaded}", "" + formatSize(uploadedTotal));
                        replaceAll(torrent_template, "{torrents_total_downloaded}", "" + formatSize(downloadedTotal));
                        // replace the total-peers-sum before we replace {iterator_torrents_template} because maybe some n00b insert the stuff there and wonder of wrong values
                        
                        replaceAll(torrent_template, "{iterator_torrents_template}", new String(bufTorrents.toString()));
                        bufTorrents = null;
                    } // iterator_torrents_template != null
                }
                replaceAll(content, "{torrent_template}", new String(torrent_template.toString()));
                
            }
        }
    }
    
    //keyword {clients_template} toDo: after testphase cleanup and make a few small methods
    private void doClients(String theme, StringBuffer content, HttpServletRequest req, HttpServletResponse resp){
        if(content.indexOf("{clients_template}") != -1){
            StringBuffer clients_template = getContent(theme, CLIENTS_TEMPLATE);
            if(clients_template == null){
                try{
                    fireDisplayError(theme, CLIENTS_TEMPLATE, req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }else{
                //first of all replace the user settings!
                if(useUserConfig){
                    replaceAllUserKeys(clients_template,theme);
                }
                
                //String peerParam = null;
                //peerParam = req.getParameter("c");
                String  clientParam = null;
                clientParam = req.getParameter("clients");
                String sortByParam = req.getParameter("sortby");
                boolean descending = false;
                String descByParam = req.getParameter("desc");
                if(descByParam != null && descByParam.equals("true"))
                    descending = true;
                else
                    descending = false;
                /* troublemakers ... nonce vs. POST
                 * just to remember ... me for POST replies
                 * and <meta http-equiv="refresh" content="30;">
                 *
                 * or in case the user send the datas twice (refresh-page)
                 *
                String action = req.getParameter("action");
                if (action == null) {
                    _log.warn("action == null" );
                    // noop
                }else  if("???".equals(action)){
                    String banParam = req.getParameter("ban");
                }
                 * instead a nonce value we use a POST
                 * so we do not reply the URL settings on next meta-refresh
                 */
                
                String banParam = req.getParameter("ban");
                String removeClientParam = req.getParameter("removec");
                String torrentParam = req.getParameter("torrent");
                if(clients_template.indexOf("{iterator_clients_template}") != -1){
                    StringBuffer iterator_clients_template = getContent(theme, CLIENT_ITERATOR);
                    if(iterator_clients_template != null){
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(iterator_clients_template,theme);
                        }
                        StringBuffer template_client_info = getContent(theme, CLIENT_INFO_TEMPLATE); //preload (needed in peer--iterator
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(template_client_info,theme);
                        }
                        StringBuffer iterator_clients_torrent_template = getContent(theme, CLIENT_TORRENTS_ITERATOR); //preload (needed in peer--iterator
                        //first of all replace the user settings!
                        if(useUserConfig){
                            replaceAllUserKeys(iterator_clients_torrent_template,theme);
                        }
                        StringBuffer bufClients = new StringBuffer();
                        
                        java.util.List clients = Client.instance().clientsXL;
                        //synchronized(Client.instance().clientsXL) {
                        if(sortByParam != null){
                            if(sortByParam != null && sortByParam.equals("name")){
                                clients = Client.instance().sortByName(descending);
                            }else if(sortByParam != null && sortByParam.equals("btclient")){
                                clients = Client.instance().sortByBTClient(descending);
                            }else if(sortByParam != null && sortByParam.equals("ct")){
                                clients = Client.instance().sortByConnectionTime(descending);
                            }else if(sortByParam != null && sortByParam.equals("fs")){
                                clients = Client.instance().sortByKnownSince(descending);
                            }else if(sortByParam != null && sortByParam.equals("ls")){
                                clients = Client.instance().sortByLastSeen(descending);
                            }else if(sortByParam != null && sortByParam.equals("cs")){
                                clients = Client.instance().sortByConnectedSince(descending);
                            }else if(sortByParam != null && sortByParam.equals("torrent")){
                                clients = Client.instance().sortByTorrent(descending);
                            }else if(sortByParam != null && sortByParam.equals("ulrate")){
                                clients = Client.instance().sortByUploadRate(descending);
                            }else if(sortByParam != null && sortByParam.equals("dlrate")){
                                clients = Client.instance().sortByDownloadRate(descending);
                            }else if(sortByParam != null && sortByParam.equals("ultotal")){
                                clients = Client.instance().sortByUploadTotal(descending);
                            }else if(sortByParam != null && sortByParam.equals("dltotal")){
                                clients = Client.instance().sortByDownloadTotal(descending);
                            }else if(sortByParam != null && sortByParam.equals("rc")){
                                clients = Client.instance().sortByReconnections(descending);
                            }else if(sortByParam != null && sortByParam.equals("ufo")){
                                clients = Client.instance().sortByUploadTotalFromOther(descending);
                            }else if(sortByParam != null && sortByParam.equals("banned")){
                                clients = Client.instance().sortByBanned(descending);
                            }
                        }
                        //}
                        //{client_name}
                        if(clients != null){// should never happpens but ... better is better
                            for (int i = 0; i < clients.size(); i++) {
                                Client client = (Client)clients.get(i);
                                
                                /*
                                 * user clean up request
                                 */
                                if(removeClientParam != null && (removeClientParam.equals(client.getClientIDString()) ||
                                        removeClientParam.equals("all") ||
                                        removeClientParam.equals("inactive"))){
                                    // in all cases if we are here than we remove the client
                                    
                                    if(removeClientParam.equals("all")){
                                        client.removeFromList();
                                        i --; //toDo any reasons to care about this?!
                                        continue;
                                    }else if(torrentParam != null && removeClientParam.equals(client.getClientIDString()) &&
                                            torrentParam.equals(client.getClientTorrentName()) ){
                                        client.removeFromList();
                                        i --; //toDo any reasons to care about this?!
                                        continue;
                                    }else if(removeClientParam.equals("inactive") && !client.hasPeer()){
                                        client.removeFromList();
                                        i --; //toDo any reasons to care about this?!
                                        continue;
                                    }else{
                                        // a client that not match with the rules for deleting.
                                    }
                                    
                                    
                                    if(clients.size() <= 0)break;
                                }
                                // banning watchout
                                // not that we toogle a client for ban or unban if he is more than once in the list
                                // a boolean is enought because we get only one client to ban at each call
                                // we need not to check for every client
                            /*
                             * but still the user can press page-refresh and allowing to send the POST datas twice
                             * than we toogle again!
                             * who cares?!
                             * OK, fixed! but let the lines for a remembering
                             * fixing was a hard work - i don't want forget it
                             * and i'm sure i need this again
                             * <form method="POST" action="?themes=expert&clients={client_name}"><input type="submit" class="ban{client_isbanned}" name="ban" value="{client_isbanned}"></form>
                             */
                                //boolean banExecuted = false;
                                
                                //check first for command "banParam"
                                if(banParam != null && client.getClientIDString().equals(clientParam)){
                                    //current we ban/unban just for a single torrent (the current one)
                                    // toDo: add a global ban/unban request
                                    boolean general = true;//true means general ban/unbanning for all torrents
                                    if(banParam.equals("true")){
                                        client.unBan(general);
                                    }else{
                                        client.ban(general);
                                    }
                                    //banExecuted = true;
                                    
                                }
                                
                                if(client.getClientName().equals(clientParam) || client.getClientIDString().equals(clientParam) || clientParam.equals("all") || clientParam.equals(client.getClientMetaInfoHash())){
                                    bufClients.append(new String(iterator_clients_template.toString()));
                                    replaceAll(bufClients, "{client_isbanned}", "" + client.isBanned());
                                    replaceAll(bufClients, "{client_name}", client.getClientName());
                                    replaceAll(bufClients, "{client_id}", client.getClientIDString());// needed for banning
                                    replaceAll(bufClients, "{client_dest}", client.getClientAddress());//current not in use
                                    //ratio
                                    replaceAll(bufClients, "{client_hasbadratio}", "" + client.hasBadRatio());
                                    replaceAll(bufClients, "{client_soft}", client.getClientSoft());
                                    replaceAll(bufClients, "{client_isconnected}", "" + client.isConnected());
                                    replaceAll(bufClients, "{client_choking}", "" + client.isChoking());
                                    
                                    replaceAll(bufClients, "{client_haspeer}", "" + client.hasPeer());
                                    replaceAll(bufClients, "{client_reconnections}", "" + client.getReconnections());
                                    //times
                                    replaceAll(bufClients, "{client_first_seen}", client.getClientKnownSince());
                                    replaceAll(bufClients, "{client_last_seen}", client.getLastSeen());
                                    replaceAll(bufClients, "{client_last_try}", client.getClientLastTry());
                                    replaceAll(bufClients, "{client_connected_since}", client.getClientConnectedSince());// current peer.isConnected()
                                    replaceAll(bufClients, "{client_connection_time}", client.getClientConnectionTime());// all times connection of this i2p session
                                    replaceAll(bufClients, "{client_last_seen_by_tracker_req}", client.getClientLastSeenByTrackerRequest());// all times connection of this i2p session
                                    replaceAll(bufClients, "{client_tracker_pings}", "" + client.getTrackerPings());// all times connection of this i2p session
                                    
                                    replaceAll(bufClients, "{client_torrent_name}", client.getClientTorrentName());
                                    replaceAll(bufClients, "{client_metainfohash}", client.getClientMetaInfoHash());
                                    
                                    //float pct = (float) (100.0 * (float) piecesClient / metainfo.getPieces());
                                    String ps = String.valueOf(client.getCompletedPercent());
                                    if (ps.length() > 5)
                                        ps = ps.substring(0, 5);
                                    replaceAll(bufClients, "{client_percent}", "" + ps);
                                    
                                    //dlrate
                                    replaceAll(bufClients, "{client_dlrate}", "" + formatSize(client.getDownloadRate()) + "ps");
                                    //peersTotalUploadRate += peer.getUploadRate();
                                    //ulrate
                                    replaceAll(bufClients, "{client_ulrate}", "" + formatSize(client.getUploadRate()) + "ps");
                                    //peersTotalDownloadRate += peer.getDownloadRate();
                                    // uploadTotal
                                    replaceAll(bufClients, "{client_ultotal}", "" + formatSize(client.getUploadTotal()));
                                    //peersTotalUpload += peer.getUploadTotal();
                                    replaceAll(bufClients, "{client_dltotal}", "" + formatSize(client.getDownloadTotal()));
                                    //
                                    // uploadTotal
                                    replaceAll(bufClients, "{client_ultotal_session}", "" + formatSize(client.getUploadTotalSession()));
                                    //peersTotalUpload += peer.getUploadTotal();
                                    replaceAll(bufClients, "{client_dltotal_session}", "" + formatSize(client.getDownloadTotalSession()));
                                    //uploadfromothers since we starting XL
                                    replaceAll(bufClients, "{client_ultotal_from_others}", "" + formatSize(client.getUploadTotalFromOther()));
                                    //peersTotalUpload += peer.getUploadTotal();
                                    //
                                    if((template_client_info != null && (showClients) && client.getClientName().equals(clientParam)) ||
                                            (template_client_info != null && (showClients) && client.getClientIDString().equals(clientParam)) ||
                                            (template_client_info != null && (showClients) && clientParam.equals("all")) ||
                                            (template_client_info != null && (showClients) && clientParam.equals(client.getClientMetaInfoHash()))
                                            ){
                                        
                                        if( true){//isRunning && curPeers > 0) { // under construction
                                            //replaceAll(bufClients, "{template_client_info}", new String(template_client_info.toString()));
                                            
                                            //StringBuffer template_torrent_peers = getContent(theme, PEERS_TEMPLATE);
                                            if(template_client_info != null ){
                                                StringBuffer bufMoreInfo = new StringBuffer();
                                                bufMoreInfo.append(new String(template_client_info.toString()));
                                                replaceAll(bufMoreInfo, "{client_id}", client.getClientIDString());
                                                replaceAll(bufMoreInfo, "{client_dest}", client.getClientAddress());
                                                replaceAll(bufMoreInfo, "{client_last_seen_by_tracker_req}", client.getClientLastSeenByTrackerRequest());// all times connection of this i2p session
                                                replaceAll(bufMoreInfo, "{client_tracker_pings}", "" + client.getTrackerPings());// all times connection of this i2p session
                                                replaceAll(bufMoreInfo, "{client_metainfohash}", client.getClientMetaInfoHash());
                                                replaceAll(bufMoreInfo, "{client_clientidhash}", client.getClientIDHash());
                                                //List torrents = client.torrentList();
                                                //torrents = getSortedTorrents(torrents);
                                                if(iterator_clients_torrent_template != null){
                                                    //StringBuffer bufNEW = new StringBuffer();
                                                }
                                                //
                                                replaceAll(bufClients, "{template_client_info}",  new String(bufMoreInfo.toString()));
                                                bufMoreInfo = null;
                                            }
                                        }
                                    }
                                    
                                    
                                    replaceAll(bufClients, "{template_client_info}", "");
                                }
                            }//for (int i = 0 ..
                        } // clients != null
                        replaceAll(clients_template, "{iterator_clients_template}", new String(bufClients.toString()));
                        bufClients = null;
                    } // iterator_torrents_template != null
                }
                replaceAll(content, "{clients_template}", new String(clients_template.toString()));
                replaceAll(content, "{clients_known}", "" + Client.instance().clientsXL.size());
                //Client.instance().clientsXL;
            }
        }
    }
    
    
    //keyword {configs_template} toDo: after testphase cleanup and make a few small methods
    private void doConfigs(String theme, StringBuffer content, HttpServletRequest req, HttpServletResponse resp){
        if(content.indexOf("{configs_template}") != -1){
            StringBuffer configs_template = getContent(theme, CONFIGS_TEMPLATE);
            if(configs_template == null){
                try{
                    fireDisplayError(theme, CONFIGS_TEMPLATE, req, resp);
                } catch (IOException ioe) {
                    _log.warn("hrm: " + ioe);
                    return;
                }
            }else{
                //first of all replace the user settings!
                if(useUserConfig){
                    replaceAllUserKeys(configs_template,theme);
                }
                
                ////
                
                //params
                
                //{}
                replaceAll(content, "{configs_template}", new String(configs_template.toString()));
            }
        }
    }
    
    
    private ArrayList getRequestedPieces(Peer peer){
        // before calling this methode check that  (!snark.coordinator.halted() && peer.isConnected() ) (peer.state not null)
        //
        int[] arr = peer.getRequestedPieces();
        ArrayList pieces = null;
        
        if(arr.length > 0){
            pieces = new ArrayList();
            for(int i = 0; i < arr.length ;i++){
                if(!pieces.contains(arr[i]) && arr[i] > - 1){//terminated by -1
                    pieces.add(arr[i]);
                }
            }
        }
        
        
        return pieces;
    }
    
    private static void replaceAll(StringBuffer buffer, String tag, String newContent) {
        int start = 0;
        boolean found = false;
        if (buffer != null && tag != null && newContent !=null){
            while ((start = buffer.indexOf(tag, start)) != -1) {
                buffer.replace(start, start+tag.length(), newContent);
                start += newContent.length();
                found = true;
            }
        }//toDo else do something
    }
    
    // rounding makes us look faster :)
    private String formatSize(long bytes) {
        if (bytes < 5*1024)
            return bytes + "B";
        else if (bytes < 5*1024*1024)
            return ((bytes + 512)/1024) + "KB";
        else if (bytes < 5*1024*1024*1024l)
            return ((bytes + 512*1024)/(1024*1024)) + "MB";
        else
            return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "GB";
    }
    
    public synchronized void setManager(SnarkManager manager){
        _manager = manager;
    }
    
    private static boolean existsUserCongig(String theme){
        String userConfigFileTmp = _templateConfig.getDefaultProberty("key_UserConfig",TEMPLATE_PATH + theme);
        
        if(userConfigFileTmp != null){
            userConfigFileTmp = userConfigFileTmp.trim();
        }else{//something is wrong with the config-file; proberly the key "key_UserConfig" is complete missing
            return false;
        }
        
        if(userConfigFileTmp != null && userConfigFileTmp.startsWith("user_") && userConfigFileTmp.endsWith(".config")){
            File ucfg = new File(TEMPLATE_PATH + theme + "/" + userConfigFileTmp);
            if (ucfg.exists()) {
                ucfg = null;
                return true;
            }
            ucfg = null;
        }
        return false;
    }
    
    private void replaceAllUserKeys(StringBuffer buf , String theme){
        ArrayList userKeys = _templateConfig.getUserKeys(userConfigFile ,TEMPLATE_PATH + theme);
        if(userKeys != null){
            for(int i = 0; i < userKeys.size(); i++){
                replaceAll(buf,"{" + (String) userKeys.get(i) + "}", _templateConfig.getUserProberty((String)userKeys.get(i)));
            }
        }
    }
    
    /**
     * Get a client description (name and version) from the given peerID byte array.
     * @param peer_id peerID sent in handshake
     * @return description
     */
    public static String getClientDescription( byte[] peer_id ) {
        return BTPeerIDByteDecoder.decode( peer_id );
    }
    
    //removed
  /*
    private String getUserClient(String sub){
        int type = 0;
        //CwsL
        if(sub.equals( "AwMD")) type = I2PSNARK;
        else
            if(sub.equals( "CwsL")) type = I2PSNARKXL;
            else
                if(sub.equals( "BFJT")) type = I2PRUFUS;
                else
                    if(sub.equals( "TTMt")) type = I2P_BT;
                    else
                        if(sub.equals( "LUFa")) type = AZUREUS;
                        else
                            if(sub.equals( "AkZV")) type = ROBERT;
   
        switch (type) {
            case I2PSNARK:
                return "I2PSnark";
            case I2PSNARKXL:
                return "XL";
            case I2PRUFUS:
                return "I2PRufus";
            case I2P_BT:
                return "I2P-BT";
            case AZUREUS:
                return "Azureus";
            case ROBERT:
                return "Robert";
            default:
                return "(" + sub +") " +"Unknown";
        }
    }
   */
    
    //temporar added method
    //see peer.java getAllPieces()
    //fwd toDo: deprecated after testphase or switch to better performed logic
    public ArrayList getAllPieces(BitField bitfield){
        ArrayList allPieces = new ArrayList();
        if(bitfield != null){
            for(int i = 0; i < bitfield.size(); i++)
                if(bitfield.get(i))
                    if(!allPieces.contains(i)){
                allPieces.add(i);
                    }
            return allPieces;
        }
        return null;
    }
    
    public String getIDString(byte[] idExt) {
        // before calling this methode check that  (!snark.coordinator.halted() ))
        
        Destination address = I2PSnarkUtil.instance().getDestination( I2PSnarkUtil.instance().getOurIPString());
        int nonZero = 0;
        for (int i = 0; i < idExt.length; i++) {
            if (idExt[i] != 0) {
                nonZero = i;
                break;
            }
        }
        return Base64.encode(idExt, nonZero, idExt.length-nonZero).substring(0,4) + "@" + address.toBase64().substring(0,6);
    }
    
    
    public static BufferedImage getImageBarNull(int width, int height){
        
        BufferedImage bufferedImage= new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        
        Graphics2D g2d = bufferedImage.createGraphics();
        g2d.setColor(Color.red);
        g2d.fill(new Rectangle2D.Float(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()));
        g2d.dispose();
        //g2.setBackground(Color.blue);
        return bufferedImage;// Image Toolkit.getDefaultToolkit().createImage(bufferedImage.getSource());
    }
    
    
    
}
