package bezeroa;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Semaphore;
import javax.swing.JProgressBar;
import erabilgarriak.DownloadFile;
import erabilgarriak.FileData;
import erabilgarriak.ServerPackage.DownloadFileArrayHolder;
public class Deskarga extends Thread {
public static final int JASOTZAILE_MAX = 5;
private ArrayList<DownloadFile> seeds;
private ArrayList<DownloadFile> seedsErabiltzen;
private ArrayList<Integer> parteak;
//Array hau igoal ez da beharrezkoa, erroreren bat badago berriro ere parteen array-ra sartuko direlako eta
//programa ixten bada ez da fitxategian gordeta egongo deskargatua izan dela
//private ArrayList<Integer> deskargatzen;
private ArrayList<Jasotzailea> jasotzaileak;
private ArrayBlockingQueue<Part> gordetzeko;
private Gordetzailea g;
private int partCount = -1;
private int downloadedParts = 0;
private Semaphore jasoEM;
private Semaphore seederEM;
private Semaphore amaituta;
private FileData file;
private SeedChecker checker;
private int jasotzaileAktibo = 0;
private JProgressBar pb;
/**
* Ez pasatu seed-ak, file data bat pasatu, run egitean seedCheker klasea martxan jarri eta seed bat egon arte
* itxaroten jarri, horrela deskarga bat berrabiaraztean ere ongi doa
*
* partCount aldagaia bertan kalkulatu behar da, tamainaren arabera eta ez kanpotik eskatu
*
*
* @param seeds
* @throws Exception
*/
public Deskarga(FileData file) throws Exception{
this.file = file;
parteak = new ArrayList<Integer>();
seeds = new ArrayList<DownloadFile>();
seedsErabiltzen = new ArrayList<DownloadFile>();
partCount = (int) Math.ceil(((double)file.size)/Globalak.eMandoa.PART_SIZE);
g = new Gordetzailea(file, false);
checker = new SeedChecker(file, this);
//Deskargatzeko dauden parteen array-a bete
for(int i=0; i<partCount; i++){
parteak.add(i);
}
gordetzeko = new ArrayBlockingQueue<Part>(100);
jasotzaileak = new ArrayList<Jasotzailea>();
jasoEM = new Semaphore(1);
seederEM = new Semaphore(1);
amaituta = new Semaphore(0);
}
public Deskarga(FileData file, ArrayList<Integer> parteak) throws Exception{
this.file = file;
this.parteak = parteak;
seeds = new ArrayList<DownloadFile>();
seedsErabiltzen = new ArrayList<DownloadFile>();
partCount = (int) Math.ceil(((double)file.size)/Globalak.eMandoa.PART_SIZE);
downloadedParts = partCount - parteak.size();
g = new Gordetzailea(file, true);
checker = new SeedChecker(file, this);
gordetzeko = new ArrayBlockingQueue<Part>(100);
jasotzaileak = new ArrayList<Jasotzailea>();
jasoEM = new Semaphore(1);
seederEM = new Semaphore(1);
amaituta = new Semaphore(0);
}
public FileData getFileData(){return file;}
public void setProgressBar(JProgressBar pb){
this.pb = pb;
this.pb.setMaximum(partCount);
this.pb.setValue(partCount - parteak.size());
}
private void jasotzaileaGehitu(){jasoEM.acquireUninterruptibly(); jasotzaileAktibo++; jasoEM.release();}
private void jasotzaileaKendu(){jasoEM.acquireUninterruptibly(); jasotzaileAktibo--; jasoEM.release();}
public void run(){
g.start();
checker.start();
//SeedChecker enkargatuko da jasotzaileak hasieratzeaz
//Amaitu arte itxaron
amaituta.acquireUninterruptibly();
checker.interrupt();
//Jasotzaileak join (Honetan kontrolatu behar da ea denekin egiten duen join-a edo hasieran daudenekin bakarrik,
//iteratzaileak hasierako balioen kopia bat egiten du edo dinamikoa da?)
for(Jasotzailea jaso : jasotzaileak){
try {
jaso.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Gordetzailea gelditu
//Exekuzioa bukatu behar da
g.interrupt();
try {
g.join();
checker.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("deskarga bukatuta");
}
public synchronized int getNextPart(){
if(parteak.size() != 0){
int part = parteak.get(0);
parteak.remove(0);
return part;
}
return -1;
}
public void putPart(Part part){
try {
gordetzeko.put(part);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
*
* Hari bakar bat egon daiteke seede-etan ibiltzen aldi bakoitzean, bestela arazoak egon daitezke
*
* semaforoan release eta
* Seeder-en bat libre badago jasotzaileari ematen zaio seed hori, bestela seedCheker funtzioak sortuko du
* seeder berriak daudenean
*
*
* @param numPart
*/
public void error(int numPart, DownloadFile df, Jasotzailea jaso){
boolean hasita=false;
parteak.add(numPart);
try {
seederEM.acquire();
seeds.remove(df);
seedsErabiltzen.remove(df);
//Uste dut hau egina dagoela, konprobatu!!!
if(seeds.size() > seedsErabiltzen.size()){
//Hari bakarra egon daiteke seeder bat bilatzen
for(DownloadFile seed : seeds){
if(!seedsErabiltzen.contains(seed)){
seedsErabiltzen.add(seed);
jaso.setSeeder(seed);
hasita = true;
break;
}
}
seederEM.release();
}
//Ezin izan bada hasi jasotzailea hilko da
if(!hasita){
jasotzaileaKendu();
jaso.hil();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Gordetzailea extends Thread{
private boolean stopped;
private RandomAccessFile ra;
private BufferedWriter bw;
public Gordetzailea(FileData file, boolean isRestart){
try {
//Deskargatzeko fitxategia sortu
ra = new RandomAccessFile(new File("./incoming/"+file.name), "rw");
//Deskargaren progresoa gordetzen duen fitxategia sortu
bw = new BufferedWriter(new FileWriter("./incoming/"+file.name+".data", isRestart));
if(!isRestart){
bw.write(file.name);
bw.newLine();
bw.write(String.valueOf(file.size));
bw.newLine();
bw.write(file.hash);
bw.newLine();
bw.write(String.valueOf(partCount));
bw.newLine();
bw.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void interrupt(){
stopped = true;
super.interrupt();
}
public void run(){
while(!stopped){
try {
Part part = gordetzeko.take();
try {
//System.out.println("Punteroaren posizioa: "+part.getNumPart()*Deskarga.PART_SIZE);
ra.seek(part.getNumPart()*Globalak.eMandoa.PART_SIZE);
//System.out.println("Gordetzen zatia: "+part.getNumPart());
ra.write(part.getPart());
//Datu fitxategian partea deskargatu dela gorde
bw.write(part.getNumPart()+"");
bw.newLine();
bw.flush();
downloadedParts++;
if(pb != null){
pb.setValue(downloadedParts);
}
if(downloadedParts == partCount){
stopped = true;
amaituta.release();
bw.close();
ra.close();
new File("./incoming/"+file.name+".data").delete();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
System.out.println("Gordetzailea gelditzen");
}
}
}
}
private class SeedChecker extends Thread{
private FileData data;
private boolean stopped;
private Deskarga des;
public SeedChecker(FileData file, Deskarga des){
this.data = file;
stopped = false;
this.des = des;
}
public void interrupt(){
stopped = true;
super.interrupt();
}
public void run(){
while(!stopped){
try {
//zerbitzariari seed gehiago eskatu
DownloadFileArrayHolder files = new DownloadFileArrayHolder(new DownloadFile[1]);
if(Globalak.eMandoa.getServer().getFile(data, files)){
//List<DownloadFile> lista = Arrays.asList(files.value);
ArrayList<DownloadFile> lista = new ArrayList<DownloadFile>();
for(DownloadFile df : files.value){
lista.add(df);
}
System.out.println("Seed kopurua: "+files.value.length);
//seeder-ak bueltatzen baditu jada seeder arraian ez daudela konprobatu
seederEM.acquire();
ArrayList<DownloadFile> bufferRemove = new ArrayList<DownloadFile>();
for(DownloadFile df : lista){
//Seeder-en konexioa konprobatu
try{
if(df._non_existent()){
bufferRemove.add(df);
}else{
for(DownloadFile seed : seeds){
if(seed._is_equivalent(df))
bufferRemove.add(df);
}
}
}catch(Exception e){
bufferRemove.add(df);
}
}
for(DownloadFile df : bufferRemove){
lista.remove(df);
}
//listan oraindik elementuren bat badago eta jasotzaile kopurua 5 baino txikiagoa bada jasotzaile berriak sortu
for(DownloadFile df : lista){
if(jasotzaileAktibo < Deskarga.JASOTZAILE_MAX){
Jasotzailea buff = new Jasotzailea(df, des);
seedsErabiltzen.add(df);
seeds.add(df);
buff.start();
jasotzaileaGehitu();
jasotzaileak.add(buff);
}else{
seeds.add(df);
}
}
seederEM.release();
}
if(!stopped)
sleep(1*60*1000);
} catch (InterruptedException e) {}
}
}
}
}